require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
module.exports = function (css, customDocument) {
var doc = customDocument || document;
if (doc.createStyleSheet) {
var sheet = doc.createStyleSheet()
sheet.cssText = css;
return sheet.ownerNode;
} else {
var head = doc.getElementsByTagName('head')[0],
style = doc.createElement('style');
style.type = 'text/css';
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(doc.createTextNode(css));
}
head.appendChild(style);
return style;
}
};
module.exports.byUrl = function(url) {
if (document.createStyleSheet) {
return document.createStyleSheet(url).ownerNode;
} else {
var head = document.getElementsByTagName('head')[0],
link = document.createElement('link');
link.rel = 'stylesheet';
link.href = url;
head.appendChild(link);
return link;
}
};
},{}],2:[function(require,module,exports){
(function (global){
/**
* The ITSA module is an aggregator for all the individual modules that the library uses.
* The developer is free to use it as it is or tailor it to contain whatever modules
* he/she might need in the global namespace.
*
* The modules themselves work quite well independent of this module and can be used
* separately without the need of them being integrated under one globa namespace.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module itsa.build
*
*/
(function (window) {
"use strict";
/**
* The ITSA class provides the core functionality for the ITSA library
* and is the root namespace for all the additional modules.
*
* The ITSA class cannot be instantiated.
* Instead, the ITSA function takes a configuration object to allow for tailoring of the library.
* The ITSA function returns itself to allow for further chaining.
*
* Calling the ITSA function is optional. If the default configuration is acceptable,
* the ITSA class can be used directly.
*
* The ITSA name is usually used only once in an application, when configuring it
* and when calling the [`ready`](#method_ready) or [`require`](#method_require) methods.
* The callback to these two methods provide a reference to ITSA itself as their argument.
* These methods allow the developer to rename ITSA to a shorter name, usually `P`,
* for use within the local scope.
*
* ITSA( config )
* .require('dialog', 'event', ...)
* .then(function (P) {
* // P is an alias of ITSA
* });
*
* // If the default configuration is acceptable, you can simply do:
* ITSA.require('dialog', 'event', ...)
* .then(function (P) {
* // P is an alias of ITSA
* });
*
* // If extra modules are to be loaded later, you can simply do:
* ITSA( config ).ready
* .then(function (P) {
* // P is an alias of ITSA
* });
*
* // And if no configuration is needed:
* ITSA.ready
* .then(function (P) {
* // P is an alias of ITSA
* });
*
*
*
* @class ITSA
* @static
* @param config {Object} Configuration options for the ITSA Library
* @return self {Object}
*/
require('css');
require('polyfill/polyfill.js'); // want the full version
var jsExt = require('js-ext/js-ext.js'), // want the full version: include it at the top, so that object.merge is available
createHashMap = require('js-ext/extra/hashmap.js').createMap;
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
/*jshint boss:true */
if (window._ITSAmodules.ITSA) {
/*jshint boss:false */
return window._ITSAmodules.ITSA; // ITSA was already created
}
var ITSA = function (config) {
ITSA._config.merge(config, {force: true});
return ITSA;
};
/**
* Global configuration properties for the ITSA object.
* It can only be set on initialization via the [`ITSA`](#docs-main) function.
*
* The config is set at a default-configutation
*
* @property _config
* @type Object
* @private
*/
ITSA._config = {
debug: true,
base: '/components'
};
/**
* Reference to `Classes` in [js-ext/extra/classes.js](../modules/js-ext.html)
*
* @property Classes
* @type Object
* @static
*/
/**
* Reference to the `LightMap`-Class in [js-ext/extra/lightmap.js](../modules/js-ext.html)
*
* @property LightMap
* @type Class
* @static
*/
/**
* Reference to the `createHashMap` function in [js-ext/extra/hashmap.js](../modules/js-ext.html)
*
* @property createHashMap
* @type function
* @static
*/
// Note: we can only merge them after je-ext is required --> ITSA.merge is only then available
ITSA.merge(jsExt);
require('window-ext')(window);
var fakedom = window.navigator.userAgent==='fake',
Event = fakedom ? require('event') : require('event-mobile')(window),
io_config = {
// timeout: 3000,
debug: true,
base: '/build'
},
dragdrop;
require('vdom')(window);
/**
* Reference to the `idGenerator` function in [utils](../modules/utils.html)
*
* @property idGenerator
* @type function
* @static
*/
require('node-plugin')(window);
require('constrain')(window);
require('panel')(window);
ITSA.merge(require('utils'));
ITSA.RESERVED_WORDS = require('js-ext/extra/reserved-words.js');
if (!fakedom) {
require('event-dom/extra/hover.js')(window);
require('event-dom/extra/valuechange.js')(window);
require('event-dom/extra/blurnode.js')(window);
require('event-dom/extra/focusnode.js')(window);
// setup dragdrop:
dragdrop = require('drag-drop')(window);
ITSA.DD = dragdrop;
ITSA.DD.init();
require('focusmanager')(window);
}
ITSA.merge(require('messages'));
require('dialog')(window);
require('scrollable')(window);
/**
* Reference to the [IO](io.html) object
* @property IO
* @type Object
* @static
*/
ITSA.IO = require('io/extra/io-transfer.js')(window);
ITSA.IO.config.merge(io_config);
require('io/extra/io-cors-ie9.js')(window);
require('io/extra/io-stream.js')(window);
require('io/extra/io-xml.js')(window);
/**
* Reference to the [UserAgent](useragent.html) object
* @property UA
* @type Object
* @static
*/
ITSA.UA = require('useragent')(window);
/**
* [Event](Event.html)-instance
* @property Event
* @type Event
* @static
*/
ITSA.Event = Event;
window._ITSAmodules.ITSA = ITSA;
module.exports = ITSA;
})(global.window || require('node-win'));
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"constrain":3,"css":178,"dialog":180,"drag-drop":1938,"event":2864,"event-dom/extra/blurnode.js":2660,"event-dom/extra/focusnode.js":2661,"event-dom/extra/hover.js":2662,"event-dom/extra/valuechange.js":2663,"event-mobile":2758,"focusmanager":2881,"io/extra/io-cors-ie9.js":3188,"io/extra/io-stream.js":3189,"io/extra/io-transfer.js":3190,"io/extra/io-xml.js":3191,"js-ext/extra/hashmap.js":3239,"js-ext/extra/reserved-words.js":3241,"js-ext/js-ext.js":3242,"messages":3259,"node-plugin":3305,"node-win":3476,"panel":5146,"polyfill/polyfill.js":5153,"scrollable":5833,"useragent":5849,"utils":5850,"vdom":5908,"window-ext":5909}],3:[function(require,module,exports){
"use strict";
module.exports = function (window) {
require('node-plugin')(window);
var createHashMap = require('js-ext/extra/hashmap.js').createMap,
PluginConstrain;
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
/*jshint boss:true */
if (PluginConstrain=window._ITSAmodules.PluginConstrain) {
/*jshint boss:false */
return PluginConstrain;
}
window._ITSAmodules.PluginConstrain = PluginConstrain = window.document.definePlugin('constrain', null, {
attrs: {
selector: 'string'
},
defaults: {
selector: 'window'
}
}
);
return PluginConstrain;
};
},{"js-ext/extra/hashmap.js":4,"node-plugin":5}],4:[function(require,module,exports){
"use strict";
var merge = function (source, target) {
var keys = Object.keys(source),
l = keys.length,
i = -1,
key;
while (++i < l) {
key = keys[i];
target[key] = source[key];
}
},
hashMap = function(members) {
// important to set the prototype to `null` --> this will exclude any Object.prototype members
var obj = Object.create(null);
members && merge(members, obj);
return obj;
};
module.exports = {
createMap: hashMap
};
},{}],5:[function(require,module,exports){
"use strict";
/**
* Basic NodePlugin Class for plugin's on HTMLElements.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
*
* @module node-plugin
* @class NodePlugin
* @since 0.0.1
*/
require('js-ext/lib/object.js');
require('js-ext/lib/string.js');
require('js-ext/lib/promise.js');
require('polyfill');
var createHashMap = require('js-ext/extra/hashmap.js').createMap,
fromCamelCase = function(input) {
return input.replace(/[a-z]([A-Z])/g, function(match, group) {
return match[0]+'-'+group.toLowerCase();
});
};
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.ElementPlugin) {
return; // ElementPlugin was already created
}
require('vdom')(window);
var NAME = '[ElementPlugin]: ',
Classes = require('js-ext/extra/classes.js'),
timers = require('utils/lib/timers.js'),
Event = require('event-dom')(window),
async = timers.async,
later = timers.later,
DELAY_DESTRUCTION = 5000, // must be kept below vnode.js its DESTROY_DELAY (which is currently 60000)
DOCUMENT = window.document,
NODE = 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
NODE_REMOVE = NODE+REMOVE,
NODE_INSERT = NODE+INSERT,
ATTRIBUTE_REMOVE = ATTRIBUTE+REMOVE,
ATTRIBUTE_CHANGE = ATTRIBUTE+CHANGE,
ATTRIBUTE_INSERT = ATTRIBUTE+INSERT,
Base, pluginDOM, modelToAttrs, attrsToModel, syncPlugin, pluginDOMresync;
Object.protectedProp(window, '_ITSAPlugins', createHashMap());
/*
* Inspects the DOM for Elements that have the plugin defined by their html and plugs the Plugin-Class.
*
* @method pluginDOM
* @param NewClass {Class} the class to be inspected
* @protected
* @since 0.0.1
*/
pluginDOM = function(NewClass) {
// asynchroniously we check all current elements and render when needed:
var ns = NewClass.prototype.$ns;
async(function() {
var elements = DOCUMENT.getAll('[plugin-'+ns+'="true"]', true),
len = elements.length,
element, i;
for (i=0; i<len; i++) {
element = elements[i];
element.plug(ns);
}
});
};
/*
* Inspects the DOM for Elements that have the plugin defined and and initialized. Then it will resyncs the plugin.
*
* @method pluginDOMresync
* @param NewClass {Class} the class to be inspected
* @protected
* @since 0.0.1
*/
pluginDOMresync = function(NewClass) {
// asynchroniously we check all current elements and render when needed:
var ns = NewClass.prototype.$ns;
async(function() {
var elements = DOCUMENT.getAll('[plugin-'+ns+'="true"]['+ns+'-ready="true"]', true),
len = elements.length,
element, i;
for (i=0; i<len; i++) {
element = elements[i];
syncPlugin(element[ns]);
}
});
};
/*
* Sets the config (first) and then the attribute-values into the plugin's model.
*
* @method attrsToModel
* @param plugin {Object} the plugin-instance
* @param config {Object} config
* @protected
* @since 0.0.1
*/
attrsToModel = function(plugin, config) {
var host = plugin.host,
attrs = plugin.attrs,
defaults = plugin.defaults,
ns = plugin.$ns + '-',
attrValue, validValue;
config || (config={});
// read the current ns-attributes on the node, overrule them with config and set the new attributes
attrs.each(function(value, key) {
attrValue = config[key] || host.getAttr(ns+key) || defaults[key];
attrValue = String(attrValue);
if (attrValue) {
switch (value.toLowerCase()) {
case 'boolean':
validValue = attrValue.validateBoolean();
attrValue = (attrValue==='true');
break;
case 'number':
validValue = attrValue.validateFloat();
attrValue = parseFloat(attrValue);
break;
case 'date':
validValue = attrValue.validateDate();
attrValue = attrValue.toDate();
break;
case 'string':
validValue = true;
break;
default:
validValue = false;
}
}
else if (value.toLowerCase()==='boolean') {
// undefined `boolean` attributes need to be stored as `false`
validValue = true;
attrValue = false;
}
else {
validValue = false;
}
if (validValue && !plugin.model[key]) {
plugin.model[key] = attrValue;
}
});
};
/*
* Sets the plugin.model properties into the attributes. Only those properties that are specified by `attrs` are set.
*
* @method modelToAttrs
* @param plugin {Object} the plugin-instance
* @protected
* @since 0.0.1
*/
modelToAttrs = function(plugin) {
console.log(NAME+'modelToAttrs');
var attrs = plugin.attrs,
model = plugin.model,
domElement = plugin.host,
ns = plugin.$ns,
newAttrs = [];
attrs.each(function(value, key) {
model[key] && (model[key]!=='undefined') && (newAttrs[newAttrs.length] = {name: ns+'-'+fromCamelCase(key), value: model[key]});
});
if (newAttrs.length>0) {
domElement.setAttrs(newAttrs, true);
}
};
/*
* Syncs the plugin: both sets the attributes as well invoking `sync`.
*
* @method syncPlugin
* @param plugin {Object} the plugin-instance
* @param compareWithPrevData {Boolean} whether to sync "no matter what" or only when pervious modeldata was changed.
* @protected
* @since 0.0.1
*/
syncPlugin = function() {
var plugin = this; // is bound with the plugin
modelToAttrs(plugin);
plugin.sync();
};
// extend window.Element:
window.Element && (function(HTMLElementPrototype) {
/**
* Checks whether the plugin is plugged in at the HtmlElement. Checks whether all its attributes are set.
*
* @for HTMLElement
* @method isPlugged
* @param plugin {String} The name of the plugin that should be plugged. Needs to be the Class, not an instance!
* @return {Boolean} whether the plugin is plugged in
* @since 0.0.1
*/
HTMLElementPrototype.isPlugged = function(plugin) {
// to prevent the need os waiting for initialisation, we will check the attribute
return (this.getAttr('plugin-'+plugin)==='true');
};
/**
* Checks whether the plugin is ready to be used.
*
* @method pluginReady
* @param plugin {String} The name of the plugin that should be ready.
* @return {Promise} whether the plugin is plugged in
* @since 0.0.1
*/
HTMLElementPrototype.pluginReady = function(plugin) {
var instance = this;
instance._pluginReadyInfo || (instance._pluginReadyInfo={});
instance._pluginReadyInfo[plugin] || (instance._pluginReadyInfo[plugin]=window.Promise.manage());
return instance._pluginReadyInfo[plugin];
};
/**
* Plugs in the plugin on the HtmlElement, and gives is special behaviour by setting the appropriate attributes.
*
* @method plug
* @param plugin {String} The name of the plugin that should be plugged.
* @param [config] {Object} any config that should be passed through when the class is instantiated.
* @param [model] {Object} model to used as `ns.model`
* @return {Object|undefined} the plugin's instance, or undefined in case of an unregistered plugin
* @since 0.0.1
*/
HTMLElementPrototype.plug = function(plugin, config, model) {
var instance = this,
Plugin;
if (typeof plugin==='string') {
if (window._ITSAPlugins[plugin]) {
if (!instance._plugin || !instance._plugin[plugin]) {
instance._plugin || Object.protectedProp(instance, '_plugin', {});
Plugin = window._ITSAPlugins[plugin];
instance._plugin[plugin] = new Plugin(instance, config, model);
}
else {
console.info('ElementPlugin '+plugin+' already plugged in');
model && instance._plugin[plugin].bindModel(model);
}
return instance._plugin[plugin];
}
else {
console.warn('Plugin '+plugin+' is not registered');
}
}
};
/**
* Gets the plugin-instance of the specified plugin-name. Will fulfill as soon as the plugin is ready.
*
* @method getPlugin
* @param plugin {String} The name of the plugin that should be plugged.
* @return {Promise} the plugin-instance of the specified plugin-name
* @since 0.0.1
*/
HTMLElementPrototype.getPlugin = function(plugin) {
var instance = this;
return instance.pluginReady(plugin).then(
function() {
return instance._plugin[plugin];
}
);
};
/**
* Unplugs a NodePlugin from the HtmlElement.
*
* @method unplug
* @param PluginClass {NodePlugin} The plugin that should be unplugged. Needs to be the Class, not an instance!
* @chainable
* @since 0.0.1
*/
HTMLElementPrototype.unplug = function(plugin) {
var instance = this;
if (instance._plugin && instance._plugin[plugin]) {
instance._plugin[plugin].destroy();
}
return instance;
};
}(window.HTMLElement.prototype));
Base = Classes.createClass(
function (hostElement, config, model) {
var instance = this;
instance.host = hostElement;
instance.model = {};
attrsToModel(instance, config);
hostElement.setAttr('plugin-'+instance.$ns, 'true', true);
if (model) {
instance.bindModel(model, true);
}
else if (hostElement.getAttr(instance.$ns+'-ready')==='true') {
instance._observer = syncPlugin.bind(instance);
instance.model.observe(instance._observer);
}
modelToAttrs(instance);
},
{
/*
* Definition of all attributes: these attributes will be read during initalization and updated during `sync`
* In the dom, the attributenames are prepended with `pluginName-`. The property-values should be the property-types
* that belong to the property, this way the attributes get right casted into model.
*
* @property attrs
* @default {}
* @type Object
* @since 0.0.1
*/
attrs: {},
/*
* Any default values for attributes specified by `attrs`.
*
* @property defaults
* @default {}
* @type Object
* @since 0.0.1
*/
defaults: {},
/**
* Binds a model to the plugin, making plugin.model equals the bound model.
* Immediately syncs the plugin with the new model-data.
*
* Syncs the new vnode's childNodes with the dom.
*
* @method bindModel
* @param model {Object} the model to bind to the itag-element
* @param [mergeCurrent=false] {Boolean} when set true, current properties on the plugin's model that aren't defined
* in the new model, get merged into the new model.
* @since 0.0.1
*/
bindModel: function(model, mergeCurrent) {
console.log(NAME+'bindModel');
var instance = this,
host = instance.host;
if (Object.isObject(model) && (instance.model!==model)) {
host.removeAttr('bound-model');
instance.model.unobserve(instance._observer);
instance._observer = null;
mergeCurrent && (model.merge(instance.model, {full: true}));
instance.model = model;
if (host.getAttr(instance.$ns+'-ready')==='true') {
instance._observer = syncPlugin.bind(instance);
instance.model.observe(instance._observer);
syncPlugin.call(instance);
}
}
},
/*
* Gets invoked after the complete initialization of all constructors in the chain.
* This method assures it will happen as last stage of the initialisation.
* This method also will invoke `render` (unless render was already done on the server)
*
* @method afterInit
* @since 0.0.1
*/
afterInit: function() {
var instance = this,
ns = instance.$ns,
host = instance.host;
if (!instance._observer) {
instance._observer = syncPlugin.bind(instance);
instance.model.observe(instance._observer);
}
(host.getAttr(ns+'-ready')==='true') || instance.render();
syncPlugin.call(instance);
host.setAttr(ns+'-ready', 'true', true);
host._pluginReadyInfo || (host._pluginReadyInfo={});
host._pluginReadyInfo[ns] || (host._pluginReadyInfo[ns]=window.Promise.manage());
host._pluginReadyInfo[ns].fulfill();
},
/*
* Renders the plugin. This method is invoked only once: at the end of initialization.
* It should be used to render any nodes inside the host. Not all plugins need this.
* Defaults to NOOP.
*
* @method render
* @since 0.0.1
*/
render: function() {
// defaults to NOOP
},
/*
* Syncs plugin.model's data with the host. Not its attributes: they will be synced automaticly.
* Is invoked after every change of plugin.model's data.
*
* @method sync
* @since 0.0.1
*/
sync: function() {
// defaults to NOOP
},
/**
* Defines the `key`-property on element.model, but only when is hasn't been defined before.
*
* @method defineWhenUndefined
* @param key {String} plugin.model's property
* @param value {any} its value to be set
* @chainable
* @since 0.0.1
*/
defineWhenUndefined: function(key, value) {
var instance = this,
model = this.model;
model[key] || (model[key]=value);
return instance;
},
/*
* Cleansup the plugin. Is invoked whenever a plugin gets unplugged or its host gets removed from the dom.
*
* @method destroy
* @since 0.0.1
*/
destroy: function () {
var instance = this,
host = instance.host,
attrs = instance.attrs,
ns = instance.$ns;
instance.model.unobserve(instance.model, instance._observer);
instance._observer = null;
attrs.each(
function(value, key) {
host.removeAttr(ns+'-'+fromCamelCase(key), true);
}
);
host.removeAttr('plugin-'+ns, true);
host.removeAttr(ns+'-ready', true);
delete host._plugin[ns];
},
$ns: 'undefined-namespace'
}
);
// Whenever elements are added: check for plugins and initialize them
Event.after(['UI:'+ATTRIBUTE_CHANGE, 'UI:'+ATTRIBUTE_INSERT], function(e) {
var element = e.target,
ns, Plugin;
// to prevent less userexperience, we plug asynchroniously
async(function() {
e.changed.forEach(function(item) {
if (item.attribute.substr(0, 7)==='plugin-') {
ns = item.attribute.substr(7);
Plugin = window._ITSAPlugins[ns];
if (Plugin) {
if (item.newValue==='true') {
element.plug(ns);
console.log(NAME, 'plug: '+ns+' due to attribute change');
}
else {
element.unplug(ns);
console.log(NAME, 'unplug: '+ns+' due to attribute change');
}
}
}
});
});
});
// Whenever elements are added: check for plugins and initialize them
Event.after('UI:'+ATTRIBUTE_REMOVE, function(e) {
var element = e.target,
ns, Plugin;
// to prevent less userexperience, we plug asynchroniously
async(function() {
e.changed.forEach(function(attribute) {
if (attribute.substr(0, 7)==='plugin-') {
ns = attribute.substr(7);
Plugin = window._ITSAPlugins[ns];
if (Plugin) {
element.unplug(ns);
console.log(NAME, 'unplug: '+ns+' due to attribute removal');
}
}
});
});
});
// Whenever elements are added: check for plugins and initialize them
Event.after('UI:'+NODE_INSERT, function(e) {
var element = e.target;
// to prevent less userexperience, we plug asynchroniously
async(function() {
var attrs = element.vnode.attrs,
ns, Plugin;
attrs && attrs.each(function(value, key) {
if (key.substr(0, 7)==='plugin-') {
ns = key.substr(7);
Plugin = window._ITSAPlugins[ns];
if (Plugin) {
element.plug(ns);
console.log(NAME, 'plug: '+ns+' due to node insert with the plugin-attribute');
}
}
});
});
});
// Whenever elements are removed: check for plugins and destoy (unplug) them
Event.after('UI:'+NODE_REMOVE, function(e) {
var element = e.target;
// to prevent less userexperience, we unplug after a delay
later(function() {
var Plugin;
if (element.plugin) {
element.plugin.each(function(value, ns) {
Plugin = window._ITSAPlugins[ns];
if (Plugin) {
element.unplug(ns);
console.log(NAME, 'unplug: '+ns+' due to node removal with this plugin');
}
});
}
}, DELAY_DESTRUCTION);
});
Event.after(
['*:prototypechange', '*:prototyperemove'],
function(e) {
pluginDOMresync(e.target);
},
function(e) {
return !!e.target.prototype.$ns;
}
);
/**
* Creates a new Element-PluginClass.
*
* @method definePlugin
* @param plugin {String} the namespace of the plugin
* @param [constructor] {Function} The function that will serve as constructor for the new class.
* If `undefined` defaults to `NOOP`
* @param [prototypes] {Object} Hash map of properties to be added to the prototype of the new class.
* @return {PluginClass}
* @since 0.0.1
*/
DOCUMENT.definePlugin = function(plugin, constructor, prototypes) {
var NewClass;
if ((typeof plugin==='string') && (plugin=plugin.replaceAll(' ', '')) && (plugin.length>0) && !plugin.contains('-')) {
/*jshint boss:true */
if (NewClass=window._ITSAPlugins[plugin]) {
/*jshint boss:false */
console.warn(NAME+'definePlugin cannot redefine Plugin '+plugin+' --> already exists');
}
else {
console.log(NAME+'definePlugin');
NewClass = Base.subClass(plugin, constructor, prototypes).mergePrototypes({$ns: plugin}, true);
}
}
else {
console.warn(NAME+'definePlugin cannot create Plugin: invalid plugin: '+plugin);
}
return NewClass;
};
/**
* Returns the PluginClass that belongs with the specified `plugin`-name.
*
* @method getPluginClass
* @param plugin {String} the namespace of the plugin
* @return {PluginClass|indefined}
* @since 0.0.1
*/
DOCUMENT.getPluginClass = function(plugin) {
return window._ITSAPlugins[plugin];
};
(function(FunctionPrototype) {
var originalSubClass = FunctionPrototype.subClass;
/**
* Returns a newly created class inheriting from this class
* using the given `constructor` with the
* prototypes listed in `prototypes` merged in.
*
*
* The newly created class has the `$$super` static property
* available to access all of is ancestor's instance methods.
*
* Further methods can be added via the [mergePrototypes](#method_mergePrototypes).
*
* @example
*
* var Circle = Shape.subClass(
* function (x, y, r) {
* // arguments will automaticly be passed through to Shape's constructor
* this.r = r;
* },
* {
* area: function () {
* return this.r * this.r * Math.PI;
* }
* }
* );
*
* @method subClass
* @param plugin {String} the namespace of the plugin
* @param [constructor] {Function} The function that will serve as constructor for the new class.
* If `undefined` defaults to `NOOP`
* @param [prototypes] {Object} Hash map of properties to be added to the prototype of the new class.
* @param [chainConstruct=true] {Boolean} Whether -during instance creation- to automaticly construct in the complete hierarchy with the given constructor arguments.
* @return {Plugin|undefined} undefined when no valid namespace is given
*/
FunctionPrototype.subClass = function (plugin, constructor, prototypes /*, chainConstruct */) {
var instance = this,
NewClass;
if (instance.prototype.$ns) {
if ((typeof plugin==='string') && (plugin=plugin.replaceAll(' ', '')) && (plugin.length>0) && !plugin.contains('-')) {
/*jshint boss:true */
if (NewClass=window._ITSAPlugins[plugin]) {
/*jshint boss:false */
console.warn(NAME+'definePlugin cannot redefine Plugin '+plugin+' --> already exists');
}
else {
// change the constructor, so that it will end by calling `_finishInit`
NewClass = originalSubClass.call(instance, constructor, prototypes).mergePrototypes({$ns: plugin}, true);
window._ITSAPlugins[plugin] = NewClass;
pluginDOM(NewClass);
}
return NewClass;
}
else {
console.warn(NAME+'subClass cannot create Plugin: invalid plugin: '+plugin);
}
}
else {
// Original subclassing
return originalSubClass.apply(instance, arguments);
}
};
}(Function.prototype));
window._ITSAmodules.ElementPlugin = true;
};
},{"event-dom":6,"js-ext/extra/classes.js":101,"js-ext/extra/hashmap.js":102,"js-ext/lib/object.js":103,"js-ext/lib/promise.js":104,"js-ext/lib/string.js":105,"polyfill":117,"utils/lib/timers.js":118,"vdom":174}],6:[function(require,module,exports){
"use strict";
/**
* Integrates DOM-events to event. more about DOM-events:
* http://www.smashingmagazine.com/2013/11/12/an-introduction-to-dom-events/
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @example
* Event = require('event-dom')(window);
*
* @module event
* @submodule event-dom
* @class Event
* @since 0.0.1
*/
var NAME = '[event-dom]: ',
Event = require('event'),
later = require('utils').later,
createHashMap = require('js-ext/extra/hashmap.js').createMap,
OUTSIDE = 'outside',
REGEXP_NODE_ID = /^#\S+$/,
REGEXP_EXTRACT_NODE_ID = /#(\S+)/,
REGEXP_UI_OUTSIDE = /^.+outside$/,
TIME_BTN_PRESSED = 200,
PURE_BUTTON_ACTIVE = 'pure-button-active',
UI = 'UI:',
NODE = 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
TAP = 'tap',
CLICK = 'click',
RIGHTCLICK = 'right'+CLICK,
CENTERCLICK = 'center'+CLICK,
EV_REMOVED = UI+NODE+REMOVE,
EV_INSERTED = UI+NODE+INSERT,
EV_CONTENT_CHANGE = UI+NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = UI+ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = UI+ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = UI+ATTRIBUTE+INSERT,
mutationEventsDefined = false,
NO_DEEP_SEARCH = {},
/*
* Internal hash containing all DOM-events that are listened for (at `document`).
*
* DOMEvents = {
* 'tap': callbackFn,
* 'mousemove': callbackFn,
* 'keypress': callbackFn
* }
*
* @property DOMEvents
* @default {}
* @type Object
* @private
* @since 0.0.1
*/
DOMEvents = {};
require('js-ext/lib/string.js');
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('polyfill/polyfill-base.js');
module.exports = function (window) {
var DOCUMENT = window.document,
_domSelToFunc, _evCallback, _findCurrentTargets, _preProcessor, _setupEvents, _setupMutationListener, _teardownMutationListener,
_setupDomListener, _teardownDomListener, SORT, _sortFunc, _sortFuncReversed, _getSubscribers, _selToFunc, MUTATION_EVENTS, preventClick;
require('vdom')(window);
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.EventDom) {
return Event; // Event was already extended
}
console.warn('event-dom 1');
MUTATION_EVENTS = [EV_REMOVED, EV_INSERTED, EV_CONTENT_CHANGE, EV_ATTRIBUTE_REMOVED, , EV_ATTRIBUTE_CHANGED, EV_ATTRIBUTE_INSERTED];
/*
* Transforms the selector to a valid function
*
* @method _selToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_selToFunc = function(customEvent, subscriber) {
Event._sellist.some(function(selFn) {
return selFn(customEvent, subscriber);
});
};
/*
* Creates a filterfunction out of a css-selector. To be used for catching any dom-element, without restrictions
* of any context (like Parcels can --> Parcel.Event uses _parcelSelToDom instead)
* On "non-outside" events, subscriber.t is set to the node that first matches the selector
* so it can be used to set as e.target in the final subscriber
*
* @method _domSelToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_domSelToFunc = function(customEvent, subscriber) {
// this stage is runned during subscription
var outsideEvent = REGEXP_UI_OUTSIDE.test(customEvent),
selector = subscriber.f,
context = subscriber.o,
vnode = subscriber.o.vnode,
isCustomElement = vnode && vnode.isItag,
visibleContent = isCustomElement && !vnode.domNode.contentHidden,
nodeid, byExactId, newTarget, deepSearch;
isCustomElement = false;
console.log(NAME, '_domSelToFunc type of selector = '+typeof selector);
// note: selector could still be a function: in case another subscriber
// already changed it.
if ((!selector && !isCustomElement) || (typeof selector === 'function')) {
subscriber.n || (subscriber.n = isCustomElement ? context : DOCUMENT);
return true;
}
selector || (selector='');
nodeid = selector.match(REGEXP_EXTRACT_NODE_ID);
nodeid ? (subscriber.nId=nodeid[1]) : (subscriber.n=isCustomElement ? context : DOCUMENT);
byExactId = REGEXP_NODE_ID.test(selector);
deepSearch = !NO_DEEP_SEARCH[customEvent];
// set the selector to `subscriber._s` so that e.currentTarget can calculate it:
subscriber._s = selector;
subscriber.f = function(e) {
// this stage is runned when the event happens
console.log(NAME, '_domSelToFunc inside filter. selector: '+selector);
var node = e.target,
vnode = node.vnode,
character1 = selector && selector.substr(1),
match = false;
if (!isCustomElement || visibleContent || context.contains(node)) {
if (selector==='') {
match = true;
}
else {
// e.target is the most deeply node in the dom-tree that caught the event
// our listener uses `selector` which might be a node higher up the tree.
// we will reset e.target to this node (if there is a match)
// note that e.currentTarget will always be `document` --> we're not interested in that
// also, we don't check for `node`, but for node.matchesSelector: the highest level `document`
// is not null, yet it doesn't have .matchesSelector so it would fail
// we DON'T want this for the focus and blur event!
if (deepSearch) {
if (vnode) {
// we go through the vdom
if (!vnode.removedFromDOM) {
while (vnode && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = vnode.domNode;
}
vnode = vnode.vParent;
}
}
}
else {
// we go through the dom
while (node.matchesSelector && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the dom');
match = byExactId ? (node.id===character1) : node.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = node;
}
node = node.parentNode;
}
}
}
else {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
if (vnode) {
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
}
else {
match = node.matchesSelector && (byExactId ? (node.id===character1) : node.matchesSelector(selector));
}
match && (e.sourceTarget=node);
}
}
}
if (outsideEvent && !match) {
// there is a match for the outside-event:
// we need to set e.sourceTarget and e.target:
newTarget = DOCUMENT.getElement(selector, true);
if (newTarget) {
e.sourceTarget = node;
subscriber.t = newTarget;
}
else {
// make return `false` because the selector is not in the dom
match = true;
}
}
console.log(NAME, '_domSelToFunc filter returns '+(!outsideEvent ? match : !match));
return !outsideEvent ? match : !match;
};
return true;
};
// at this point, we need to find out what are the current node-refs. whenever there is
// a filter that starts with `#` --> in those cases we have a bubble-chain, because the selector isn't
// set up with `document` at its root.
// we couldn't do this at time of subscribtion, for the nodes might not be there at that time.
// however, we only need to do this once: we store the value if we find them
// no problem when the nodes leave the dom later: the previous filter wouldn't pass
_findCurrentTargets = function(subscribers) {
console.log(NAME, '_findCurrentTargets');
subscribers.forEach(
function(subscriber) {
console.log(NAME, '_findCurrentTargets for single subscriber. nId: '+subscriber.nId);
subscriber.nId && (subscriber.n=DOCUMENT.getElementById(subscriber.nId, true));
}
);
};
/*
* Generates an event through our Event-system. Does the actual transportation from DOM-events
* into our Eventsystem. It also looks at the response of our Eventsystem: if our system
* halts or preventDefaults the customEvent, then the original DOM-event will be preventDefaulted.
*
* @method _evCallback
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_evCallback = function(e) {
console.log(NAME, '_evCallback');
var allSubscribers = Event._subs,
eType = e.type,
eventobject, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs, subsOutside,
subscribers, eventobjectOutside, wildcard_named_subsOutside, customEvent, eventName, which;
eventName = eType;
// first: a `click` event might be needed to transformed into `rightclick`:
if (eventName===CLICK) {
which = e.which;
(which===2) && (eventName=CENTERCLICK);
(which===3) && (eventName=RIGHTCLICK);
}
if (eventName==='tap') {
// prevent the next click-event
preventClick = true;
e.clientX || (e.clientX = e.center && e.center.x);
e.clientY || (e.clientY = e.center && e.center.y);
}
else if (preventClick && (eventName===CLICK)) {
preventClick = false;
return;
}
if (eventName===CLICK) {
eventName = 'tap';
e.center = {
x: e.clientX,
y: e.clientY
};
e.eventType = 4;
e.pointerType = 'mouse';
e.tapCount = 1;
}
customEvent = 'UI:'+eventName;
subs = allSubscribers[customEvent];
wildcard_named_subs = allSubscribers['*:'+eventName];
named_wildcard_subs = allSubscribers['UI:*'];
wildcard_wildcard_subs = allSubscribers['*:*'];
// Emit the dom-event though our eventsystem:
// NOTE: emit() needs to be synchronous! otherwise we wouldn't be able
// to preventDefault in time
//
// e = eventobject from the DOM-event OR gesture-event
// eventobject = eventobject from our Eventsystem, which get returned by calling `emit()`
// now so the work:
subscribers = _getSubscribers(e, true, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
eventobject = Event._emit(e.target, customEvent, e, subscribers, [], _preProcessor, false, true);
// now check outside subscribers
subsOutside = allSubscribers[customEvent+OUTSIDE];
wildcard_named_subsOutside = allSubscribers['*:'+eventName+OUTSIDE];
subscribers = _getSubscribers(e, true, subsOutside, wildcard_named_subsOutside);
eventobjectOutside = Event._emit(e.target, customEvent+OUTSIDE, e, subscribers, [], _preProcessor, false, true);
// if eventobject was preventdefaulted or halted: take appropriate action on
// the original dom-event. Note: only the original event can caused this, not the outsideevent
// stopPropagation on the original eventobject has no impact on our eventsystem, but who know who else is watching...
// be carefull though: not all gesture events have e.stopPropagation
eventobject.status.halted && e.stopPropagation && e.stopPropagation();
// now we might need to preventDefault the original event.
// be carefull though: not all gesture events have e.preventDefault
if ((eventobject.status.halted || eventobject.status.defaultPrevented || eventobject.status.defaultPreventedContinue) && e.preventDefault) {
e.preventDefault();
}
if (eventobject.status.ok) {
// last step: invoke the aftersubscribers
// we need to do this asynchronous: this way we pass them AFTER the DOM-event's defaultFn
// also make sure to paas-in the payload of the manipulated eventobject
subscribers = _getSubscribers(e, false, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent, eventobject, [], subscribers, _preProcessor, true), 10);
// now check outside subscribers
subscribers = _getSubscribers(e, false, subsOutside, wildcard_named_subsOutside);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent+OUTSIDE, eventobjectOutside, [], subscribers, _preProcessor, true), 10);
}
};
/*
* Creates an array of subscribers in the right order, conform their position in the DOM.
* Only subscribers that match the filter are involved.
*
* @method _getSubscribers
* @param e {Object} eventobject
* @param before {Boolean} whether it is a before- or after-subscriber
* @param subs {Array} array with subscribers
* @param wildcard_named_subs {Array} array with subscribers
* @param named_wildcard_subs {Array} array with subscribers
* @param wildcard_wildcard_subs {Array} array with subscribers
* @private
* @since 0.0.1
*/
_getSubscribers = function(e, before, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs) {
var subscribers = [],
beforeOrAfter = before ? 'b' : 'a',
saveConcat = function(extrasubs) {
extrasubs && extrasubs[beforeOrAfter] && (subscribers=subscribers.concat(extrasubs[beforeOrAfter]));
};
saveConcat(subs);
saveConcat(wildcard_named_subs);
saveConcat(named_wildcard_subs);
saveConcat(wildcard_wildcard_subs);
if (subscribers.length>0) {
subscribers = function(array, testFunc) {
// quickest way to filter an array: see http://jsperf.com/array-filter-performance/4
var filtered = array.slice(0), i;
for (i=array.length-1; i>=0; i--) {
console.log(NAME, 'filtercheck for subscriber');
testFunc(array[i]) || filtered.splice(i, 1);
}
return filtered;
}(subscribers, function(subscriber) {return (!subscriber.f || subscriber.f.call(subscriber.o, e));});
if (subscribers.length>0) {
// _findCurrentTargets(subscribers);
// sorting, based upon the sortFn
subscribers.sort(SORT);
}
}
return subscribers;
};
/*
* Sets e.target and e.sourceTarget for the single subscriber.
* Needs to be done for evenry single subscriber, because with a single event, these values change for each subscriber
*
* @method _preProcessor
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_preProcessor = function(subscriber, e) {
console.log(NAME, '_preProcessor');
// inside the aftersubscribers, we may need exit right away.
// this would be the case whenever stopPropagation or stopImmediatePropagation was called
// in case the subscribernode equals the node on which stopImmediatePropagation was called: return true
var propagationStopped, immediatePropagationStopped,
targetnode = (subscriber.t || subscriber.n);
immediatePropagationStopped = e.status.immediatePropagationStopped;
if (immediatePropagationStopped && ((immediatePropagationStopped===targetnode) || !immediatePropagationStopped.contains(targetnode))) {
console.log(NAME, '_preProcessor will return true because of immediatePropagationStopped');
return true;
}
// in case the subscribernode does not fall within or equals the node on which stopPropagation was called: return true
propagationStopped = e.status.propagationStopped;
if (propagationStopped && (propagationStopped!==targetnode) && !propagationStopped.contains(targetnode)) {
console.log(NAME, '_preProcessor will return true because of propagationStopped');
return true;
}
e._s = subscriber._s;
// now we might need to set e.target to the right node:
// the filterfunction might have found the true domnode that should act as e.target
// and set it at subscriber.t
// also, we need to backup the original e.target: this one should be reset when
// we encounter a subscriber with its own filterfunction instead of selector
if (subscriber.t) {
e.sourceTarget || (e.sourceTarget=e.target);
e.target = subscriber.t;
}
else {
e.sourceTarget && (e.target=e.sourceTarget);
}
return false;
};
/*
* Transports DOM-events to the Event-system. Catches events at their most early stage:
* their capture-phase. When these events happen, a new customEvent is generated by our own
* Eventsystem, by calling _evCallback(). This way we keep DOM-events and our Eventsystem completely separated.
*
* @method _setupDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_setupDomListener = function(customEvent, subscriber) {
console.log(NAME, '_setupDomListener');
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0],
eventName = eventSplitted[1],
outsideEvent = REGEXP_UI_OUTSIDE.test(eventName);
// be careful: anyone could also register an `outside`-event.
// in those cases, the DOM-listener must be set up without `outside`
outsideEvent && (eventName=eventName.substring(0, eventName.length-7));
// if eventName equals `mouseover` or `mouseleave` then we quit:
// people should use `mouseover` and `mouseout`
if ((eventName==='mouseenter') || (eventName==='mouseleave')) {
console.warn(NAME, 'Subscription to '+eventName+' not supported, use mouseover and mouseout: this eventsystem uses these non-noisy so they act as mouseenter and mouseleave');
return;
}
// only accept tap-events, yet later on we WILL need to listen for click-events
(eventName===CLICK) && (eventName=TAP);
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(emitterName+':'+eventName+(outsideEvent ? OUTSIDE : ''), subscriber);
// already registered? then return, also return if someone registered for UI:*
if (DOMEvents[eventName] || (eventName==='*')) {
// cautious: one might have registered the event, but not yet the outsideevent.
// in that case: save this setting:
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
return;
}
DOMEvents[eventName] = true;
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
// one exception: windowresize should listen to the window-object
if (eventName==='resize') {
window.addEventListener(eventName, _evCallback);
}
else {
((eventName===RIGHTCLICK) || (eventName===CENTERCLICK)) && (eventName=TAP);
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.addEventListener(eventName, _evCallback, true);
// listen for both `tap` and `click` events to happen
(eventName===TAP) && DOCUMENT.addEventListener(CLICK, _evCallback, true);
}
};
_setupEvents = function() {
var lastFocussed;
// make sure disabled buttons don't work:
Event.before(['tap', 'press'], function(e) {
e.preventDefault();
}, '.pure-button-disabled, button[disabled]');
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.before(
'keydown',
function(e) {
e._buttonPressed = true;
Event.emit(e.target, 'UI:tap', e);
},
function(e) {
var keyCode = e.keyCode;
return (e.target.getTagName()==='BUTTON') && ((keyCode===13) || (keyCode===32));
}
);
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.after(
'tap',
function(e) {
var buttonNode = e.target;
if (e._buttonPressed) {
buttonNode.setClass(PURE_BUTTON_ACTIVE);
e._noRender = true;
// even if the node isn't in the DOM, we can still try to manipulate it:
// the vdom makes sure no errors occur when the node is already removed
later(buttonNode.removeClass.bind(buttonNode, PURE_BUTTON_ACTIVE), TIME_BTN_PRESSED);
}
}
);
// fix activeElement on Mac
Event.before('focus', function(e) {
// will come here more often because of bubblechain
// however, the last pass-through will set the deepest node
lastFocussed = e.target;
});
// patching DOCUMENT.activeElement because it doesn't work well in a Mac: https://developer.mozilla.org/en-US/docs/Web/API/document.activeElement
// DOCUMENT._activeElement is used with the patch for DOCUMENT.activeElement its getter
Event.after('focus', function() {
DOCUMENT._activeElement = lastFocussed;
});
Event.before('blur', function() {
DOCUMENT._activeElement = null;
});
// Note: window.document has no prototype
Object.defineProperty(DOCUMENT, 'activeElement', {
get: function() {
return DOCUMENT._activeElement || DOCUMENT.body;
}
});
};
_setupMutationListener = function() {
DOCUMENT.hasMutationSubs = true;
if (!mutationEventsDefined) {
Event.defineEvent(EV_REMOVED).unPreventable();
Event.defineEvent(EV_INSERTED).unPreventable();
Event.defineEvent(EV_CONTENT_CHANGE).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_REMOVED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_CHANGED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_INSERTED).unPreventable();
mutationEventsDefined = true;
}
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFunc = function(subscriberOne, subscriberTwo) {
return (subscriberTwo.t || subscriberTwo.n).contains(subscriberOne.t || subscriberOne.n) ? -1 : 1;
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFuncReversed = function(subscriberOne, subscriberTwo) {
return (subscriberOne.t || subscriberOne.n).contains(subscriberTwo.t || subscriberTwo.n) ? 1 : -1;
};
/*
* Removes DOM-eventsubscribers from document when they are no longer needed.
*
* @method _teardownDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @private
* @since 0.0.2
*/
_teardownDomListener = function(customEvent) {
var customEventWithoutOutside = customEvent.endsWith(OUTSIDE) ? customEvent.substr(0, customEvent.length-7) : customEvent,
eventSplitted = customEventWithoutOutside.split(':'),
eventName = eventSplitted[1],
stillInUse;
if ((customEventWithoutOutside===CLICK) || (customEventWithoutOutside===RIGHTCLICK) || (customEventWithoutOutside===CENTERCLICK)) {
stillInUse = Event._subs[CLICK] ||
Event._subs[CLICK+OUTSIDE];
Event._subs[RIGHTCLICK] ||
Event._subs[RIGHTCLICK+OUTSIDE],
Event._subs[CENTERCLICK] ||
Event._subs[CENTERCLICK+OUTSIDE];
eventName = CLICK;
}
else {
stillInUse = Event._subs[customEventWithoutOutside] || Event._subs[customEventWithoutOutside+OUTSIDE];
}
if (!stillInUse) {
console.log(NAME, '_teardownDomListener '+customEvent);
// remove eventlistener from `document`
// one exeption: windowresize should listen to the window-object
if (eventName==='resize') {
window.removeEventListener(eventName, _evCallback);
}
else {
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.removeEventListener(eventName, _evCallback, true);
}
delete DOMEvents[eventName];
}
};
_teardownMutationListener = function() {
if (!Event._subs[EV_REMOVED] &&
!Event._subs[EV_INSERTED] &&
!Event._subs[EV_CONTENT_CHANGE] &&
!Event._subs[EV_ATTRIBUTE_REMOVED] &&
!Event._subs[EV_ATTRIBUTE_CHANGED] &&
!Event._subs[EV_ATTRIBUTE_INSERTED]
) {
DOCUMENT.hasMutationSubs = false;
}
};
// Now a very tricky one:
// Some browsers do an array.sort down-top instead of top-down.
// In those cases we need another sortFn, for the position on an equal match should fall
// behind instead of before (which is the case on top-down sort)
[1,2].sort(function(a /*, b */) {
SORT || (SORT=(a===2) ? _sortFuncReversed : _sortFunc);
});
// Now we do some initialization in order to make DOM-events work:
Object.defineProperty(Event._defaultEventObj, 'currentTarget', {
get: function() {
var ev = this,
e_target = ev.target;
if (e_target && ev._s) {
if (e_target.matches(ev._s)) {
return e_target;
}
return e_target.inside(ev._s) || undefined;
}
}
});
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(UI+'*', _setupDomListener, Event)
._setEventObjProperty('stopPropagation', function() {this.status.ok || (this.status.propagationStopped = this.target);})
._setEventObjProperty('stopImmediatePropagation', function() {this.status.ok || (this.status.immediatePropagationStopped = this.target);});
// Notify when someone subscribes to any event at all --> we might need to transform the filterFn from a selector into a true fnction
// this is already done automaticly by _setupDomListener fo UI:* events
Event.notify('*:*', function(customEvent, subscriber) {
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0];
if ((emitterName!=='UI') && (typeof subscriber.f==='string')) {
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(customEvent, subscriber);
}
}, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(UI+'*', _teardownDomListener, Event);
Event._sellist = [_domSelToFunc];
_setupEvents();
// making HTMLElement to be able to emit using event-emitter:
(function(HTMLElementPrototype) {
HTMLElementPrototype.merge(Event.Emitter('UI'));
}(window.HTMLElement.prototype));
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(MUTATION_EVENTS, _setupMutationListener, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(MUTATION_EVENTS, _teardownMutationListener, Event);
// Note: window.document has no prototype
DOCUMENT.suppressMutationEvents = function(suppress) {
this._suppressMutationEvents = suppress;
};
// Event.noDeepDomEvt and Event._domCallback are the only method that is added to Event.
// We need to do this, because `event-mobile` needs access to the same method.
// We could have done without this method and instead listen for a custom-event to handle
// Mobile events, however, that would lead into 2 eventcycli which isn't performant.
/**
*
* @method noDeepDomEvt
* @param domEvent {String} the eventName that should be processed without deepsearch
* @param e {Object} eventobject
* @for Event
* @chainable
* @since 0.0.1
*/
Event.noDeepDomEvt = function(domEvent) {
domEvent.contains(':') || (domEvent=UI+domEvent);
NO_DEEP_SEARCH[domEvent] = true;
return this;
};
/**
* Does the actual transportation from DOM-events into the Eventsystem. It also looks at the response of
* the Eventsystem: on e.halt() or e.preventDefault(), the original DOM-event will be preventDefaulted.
*
* @method _domCallback
* @param eventName {String} the customEvent that is transported to the eventsystem
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
Event._domCallback = function(e) {
_evCallback(e);
};
// store module:
window._ITSAmodules.EventDom = Event;
return Event;
};
},{"event":10,"js-ext/extra/hashmap.js":26,"js-ext/lib/array.js":27,"js-ext/lib/object.js":28,"js-ext/lib/string.js":29,"polyfill/polyfill-base.js":41,"utils":42,"vdom":100}],7:[function(require,module,exports){
(function (global){
/**
* Defines the Event-Class, which should be instantiated to get its functionality
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
*
* @module event
* @class Event
* @constructor
* @since 0.0.1
*/
require('polyfill/polyfill-base.js');
require('js-ext/lib/object.js');
var createHashMap = require('js-ext/extra/hashmap.js').createMap;
// to prevent multiple Event instances
// (which might happen: http://nodejs.org/docs/latest/api/modules.html#modules_module_caching_caveats)
// we make sure Event is defined only once. Therefore we bind it to `global` and return it if created before
(function (global, factory) {
"use strict";
global._ITSAmodules || Object.protectedProp(global, '_ITSAmodules', createHashMap());
global._ITSAmodules.Event || (global._ITSAmodules.Event = factory());
module.exports = global._ITSAmodules.Event;
}(typeof global !== 'undefined' ? global : /* istanbul ignore next */ this, function () {
"use strict";
var NAME = '[core-event]: ',
REGEXP_CUSTOMEVENT = /^((?:\w|-|#)+):((?:\w|-|#)+)$/,
WILDCARD_WILDCARD = '*:*',
REGEXP_WILDCARD_CUSTOMEVENT = /^(?:((?:(?:\w|-|#)+)|\*):)?((?:(?:\w|-|#)+)|\*)$/,
/* REGEXP_WILDCARD_CUSTOMEVENT :
*
* valid:
* 'red:save'
* 'red:*'
* '*:save'
* '*:*'
* 'save'
*
* invalid:
* '*red:save'
* 're*d:save'
* 'red*:save'
* 'red:*save'
* 'red:sa*ve'
* 'red:save*'
* ':save'
*/
REGEXP_EMITTERNAME_WITH_SEMICOLON = /^((?:\w|-|#)+):/,
REGEXP_EVENTNAME_WITH_SEMICOLON = /:((?:\w|-|#)+)$/,
Event;
Event = {
/**
* Subscribes to a customEvent. The callback will be executed `after` the defaultFn.
*
* @static
* @method after
* @param customEvent {String|Array} the custom-event (or Array of events) to subscribe to. CustomEvents should
* have the syntax: `emitterName:eventName`. Wildcard `*` may be used for both `emitterName` as well as `eventName`.
* If `emitterName` is not defined, `UI` is assumed.
* @param callback {Function} subscriber:will be invoked when the event occurs. An `eventobject` will be passed
* as its only argument.
* @param [context] {Object} the instance that subscribes to the event.
* any object can passed through, even those are not extended with event-listener methods.
* Note: Objects who are extended with listener-methods should use instance.after() instead.
* @param [filter] {String|Function} to filter the event.
* Use a String if you want to filter DOM-events by a `selector`
* Use a function if you want to filter by any other means. If the function returns a trully value, the
* subscriber gets invoked. The function gets the `eventobject` as its only argument and the context is
* the subscriber.
* @param [prepend=false] {Boolean} whether the subscriber should be the first in the list of after-subscribers.
* @return {Object} handler with a `detach()`-method which can be used to detach the subscriber
* @since 0.0.1
*/
after: function(customEvent, callback, context, filter, prepend) {
console.log(NAME, 'add after subscriber to: '+customEvent);
return this._addMultiSubs(false, customEvent, callback, context, filter, prepend);
},
/**
* Subscribes to a customEvent. The callback will be executed `before` the defaultFn.
*
* @static
* @method before
* @param customEvent {String|Array} the custom-event (or Array of events) to subscribe to. CustomEvents should
* have the syntax: `emitterName:eventName`. Wildcard `*` may be used for both `emitterName` as well as `eventName`.
* If `emitterName` is not defined, `UI` is assumed.
* @param callback {Function} subscriber:will be invoked when the event occurs. An `eventobject` will be passed
* as its only argument.
* @param [context] {Object} the instance that subscribes to the event.
* any object can passed through, even those are not extended with event-listener methods.
* Note: Objects who are extended with listener-methods should use instance.before() instead.
* @param [filter] {String|Function} to filter the event.
* Use a String if you want to filter DOM-events by a `selector`
* Use a function if you want to filter by any other means. If the function returns a trully value, the
* subscriber gets invoked. The function gets the `eventobject` as its only argument and the context is
* the subscriber.
* @param [prepend=false] {Boolean} whether the subscriber should be the first in the list of before-subscribers.
* @return {Object} handler with a `detach()`-method which can be used to detach the subscriber
* @since 0.0.1
*/
before: function(customEvent, callback, context, filter, prepend) {
console.log(NAME, 'add before subscriber to: '+customEvent);
return this._addMultiSubs(true, customEvent, callback, context, filter, prepend);
},
/**
* Defines an emitterName into the instance (emitter).
* This will add a protected property `_emitterName` to the instance.
*
* @static
* @method defineEmitter
* @param emitter {Object} instance that should hold the emitterName
* @param emitterName {String} identifier that will be added when events are sent (`emitterName:eventName`)
* @since 0.0.1
*/
defineEmitter: function (emitter, emitterName) {
console.log(NAME, 'defineEmitter: '+emitterName);
// ennumerable MUST be set `true` to enable merging
Object.defineProperty(emitter, '_emitterName', {
configurable: false,
enumerable: true,
writable: false,
value: emitterName
});
},
/**
* Defines a CustomEvent. If the eventtype already exists, it will not be overridden,
* unless you force to assign with `.forceAssign()`
*
* The returned object comes with 8 methods which can be invoked chainable:
*
* <ul>
* <li>defaultFn() --> the default-function of the event</li>
* <li>preventedFn() --> the function that should be invoked when the event is defaultPrevented</li>
* <li>forceAssign() --> overrides any previous definition</li>
* <li>unHaltable() --> makes the customEvent cannot be halted</li>
* <li>unPreventable() --> makes the customEvent's defaultFn cannot be prevented</li>
* <li>unSilencable() --> makes that emitters cannot make this event to perform silently (using e.silent)</li>
* </ul>
*
* @static
* @method defineEvent
* @param customEvent {String} name of the customEvent conform the syntax: `emitterName:eventName`
* @return {Object} with extra methods that can be chained:
* <ul>
* <li>unPreventable() --> makes the customEvent's defaultFn cannot be prevented</li>
* <li>forceAssign() --> overrides any previous definition</li>
* <li>defaultFn() --> the default-function of the event</li>
* <li>preventedFn() --> the function that should be invoked when the event is defaultPrevented</li>
* <li>forceAssign() --> overrides any previous definition</li>
* <li>unHaltable() --> makes the customEvent cannot be halted</li>
* <li>unSilencable() --> makes that emitters cannot make this event to perform silently (using e.silent)</li>
* </ul>
* @since 0.0.1
*/
defineEvent: function (customEvent) {
console.log(NAME, 'Events.defineEvent: '+customEvent);
var instance = this,
customevents = instance._ce,
extract, exists, newCustomEvent;
if (typeof customEvent!=='string') {
console.error(NAME, 'defineEvent should have a String-type as argument');
return;
}
extract = customEvent.match(REGEXP_CUSTOMEVENT);
if (!extract) {
console.error(NAME, 'defined Customevent '+customEvent+' does not match pattern');
return;
}
newCustomEvent = {
preventable: true
};
exists = customevents[customEvent];
// if customEvent not yet exists, we can add it
// else, we might need to wait for `forceAssign` to be called
if (!exists) {
// we can add it
customevents[customEvent] = newCustomEvent;
}
return {
defaultFn: function(defFn) {
newCustomEvent.defaultFn = defFn;
return this;
},
preventedFn: function(prevFn) {
newCustomEvent.preventedFn = prevFn;
return this;
},
unHaltable: function() {
newCustomEvent.unHaltable = true;
return this;
},
unSilencable: function() {
newCustomEvent.unSilencable = true;
return this;
},
unPreventable: function() {
newCustomEvent.unPreventable = true;
return this;
},
forceAssign: function() {
// only needed when not yet added:
// exists || (customevents[customEvent]=newCustomEvent);
customevents[customEvent] = newCustomEvent;
return this;
}
};
},
/**
* Detaches (unsubscribes) the listener from the specified customEvent.
*
* @static
* @method detach
* @param [listener] {Object} The instance that is going to detach the customEvent.
* When not passed through (or undefined), all customevents of all instances are detached
* @param customEvent {String} conform the syntax: `emitterName:eventName`, wildcard `*` may be used for both
* `emitterName` as well as only `eventName`, in which case 'UI' will become the emitterName.
* Can be set as the only argument.
* @since 0.0.1
*/
detach: function(listener, customEvent) {
console.log('detach instance-subscriber: '+customEvent);
// (typeof listener === 'string') means: only `customEvent` passed through
(typeof listener === 'string') ? this._removeSubscribers(undefined, listener) : this._removeSubscribers(listener, customEvent);
},
/**
* Detaches (unsubscribes) the listener from all customevents.
*
* @static
* @method detachAll
* @param listener {Object} The instance that is going to detach the customEvent
* @since 0.0.1
*/
detachAll: function(listener) {
console.log(NAME, 'detach '+(listener ? 'all instance-' : 'ALL')+' subscribers');
var instance = this;
if (listener) {
instance._removeSubscribers(listener, '*:*');
}
else {
// we cannot just redefine _subs, for it is set as readonly
instance._subs.each(
function(value, key) {
delete instance._subs[key];
}
);
}
},
/**
* Emits the event `eventName` on behalf of `emitter`, which becomes e.target in the eventobject.
* During this process, all subscribers and the defaultFn/preventedFn get an eventobject passed through.
* The eventobject is created with at least these properties:
*
* <ul>
* <li>e.target --> source that triggered the event (instance or DOM-node), specified by `emitter`</li>
* <li>e.type --> eventName</li>
* <li>e.emitter --> emitterName</li>
* <li>e.status --> status-information:
* <ul>
* <li>e.status.ok --> `true|false` whether the event got executed (not halted or defaultPrevented)</li>
* <li>e.status.defaultFn (optional) --> `true` if any defaultFn got invoked</li>
* <li>e.status.preventedFn (optional) --> `true` if any preventedFn got invoked</li>
* <li>e.status.halted (optional) --> `reason|true` if the event got halted and optional the why</li>
* <li>e.status.defaultPrevented (optional) --> `reason|true` if the event got defaultPrevented and optional the why</li>
* </ul>
* </li>
* </ul>
*
* The optional `payload` is merged into the eventobject and could be used by the subscribers and the defaultFn/preventedFn.
* If payload.silent is set true, the subscribers are not getting invoked: only the defaultFn.
*
* The eventobject also has these methods:
*
* <ul>
* <li>e.halt() --> stops immediate all actions: no mer subscribers are invoked, no defaultFn/preventedFn</li>
* <li>e.preventDefault() --> instead of invoking defaultFn, preventedFn will be invoked. No aftersubscribers</li>
* </ul>
*
* <ul>
* <li>First, before-subscribers are invoked: this is the place where you might call `e.halt()`, `a.preventDefault()`</li>
* <li>Next, defaultFn or preventedFn gets invoked, depending on whether e.halt() or a.preventDefault() has been called</li>
* <li>Finally, after-subscribers get invoked (unless e.halt() or a.preventDefault() has been called)</li>
* <ul>
*
* @static
* @method emit
* @param [emitter] {Object} instance that emits the events
* @param customEvent {String} Full customEvent conform syntax `emitterName:eventName`.
* `emitterName` is available as **e.emitter**, `eventName` as **e.type**.
* @param payload {Object} extra payload to be added to the event-object
* @return {Object|undefined} eventobject or undefined when the event was halted or preventDefaulted.
* @since 0.0.1
*/
emit: function (emitter, customEvent, payload) {
var instance = this;
if (typeof emitter === 'string') {
// emit is called with signature emit(customEvent, payload)
// thus the source-emitter is the Event-instance
payload = customEvent;
customEvent = emitter;
emitter = instance;
}
return instance._emit(emitter, customEvent, payload);
},
/**
* Creates a notifier for the customEvent.
* You can use this to create delayed `defineEvents`. When the customEvent is called, the callback gets invoked
* (even before the subsrcibers). Use this callback for delayed customEvent-definitions.
*
* You may use wildcards for both emitterName and eventName.
* You **must** specify the full `emitterName:eventName` syntax.
* The module `core-event-dom` uses `notify` to auto-define DOM-events (UI:*).
*
* @static
* @method notify
* @param customEvent {String|Array} the custom-event (or Array of events) to subscribe to. CustomEvents should
* have the syntax: `emitterName:eventName`. Wildcard `*` may be used only for`eventName`.
* If `emitterName` should be defined.
* @param callback {Function} subscriber: will be invoked when the customEvent is called (before any subscribers.
* Recieves 2 arguments: the `customEvent` and `subscriber-object`.
* @param context {Object} context of the callback
* @param [once=false] {Boolean} whether the subscriptions should be removed after the first invokation
* @chainable
* @since 0.0.1
*/
notify: function(customEvent, callback, context, once) {
console.log(NAME, 'notify');
var i, len, ce;
Array.isArray(customEvent) || (customEvent=[customEvent]);
len = customEvent.length;
for (i=0; i<len; i++) {
ce = customEvent[i];
this._notifiers[ce] = {
cb: callback,
o: context,
r: once // r = remove automaticly
};
}
return this;
},
/**
* Creates a detach-notifier for the customEvent.
* You can use this to get informed whenever a subscriber detaches.
*
* Use **no** wildcards for the emitterName. You might use wildcards for the eventName. Without wildcards, the
* notification will be unNotified (callback automaticly detached) on the first time the event occurs.
* You **must** specify the full `emitterName:eventName` syntax.
* The module `core-event-dom` uses `notify` to auto-define DOM-events (UI:*).
*
* @static
* @method notifyDetach
* @param customEvent {String|Array} the custom-event (or Array of events) to subscribe to. CustomEvents should
* have the syntax: `emitterName:eventName`. Wildcard `*` may be used only for`eventName`.
* If `emitterName` should be defined.
* @param callback {Function} subscriber: will be invoked when the customEvent is called (before any subscribers.
* Recieves 1 arguments: the `customEvent`.
* @param context {Object} context of the callback
* @param [once=false] {Boolean} whether the subscriptions should be removed after the first invokation
* @chainable
* @since 0.0.1
*/
notifyDetach: function(customEvent, callback, context, once) {
console.log(NAME, 'notifyDetach');
var i, len, ce;
Array.isArray(customEvent) || (customEvent=[customEvent]);
len = customEvent.length;
for (i=0; i<len; i++) {
ce = customEvent[i];
this._detachNotifiers[ce] = {
cb: callback,
o: context,
r: once // r = remove automaticly
};
}
return this;
},
/**
* Subscribes to a customEvent. The callback will be executed `after` the defaultFn.
* The subscriber will be automaticly removed once the callback executed the first time.
* No need to `detach()` (unless you want to undescribe before the first event)
*
* @static
* @method onceAfter
* @param customEvent {String|Array} the custom-event (or Array of events) to subscribe to. CustomEvents should
* have the syntax: `emitterName:eventName`. Wildcard `*` may be used for both `emitterName` as well as `eventName`.
* If `emitterName` is not defined, `UI` is assumed.
* @param callback {Function} subscriber:will be invoked when the event occurs. An `eventobject` will be passed
* as its only argument.
* @param [context] {Object} the instance that subscribes to the event.
* any object can passed through, even those are not extended with event-listener methods.
* Note: Objects who are extended with listener-methods should use instance.onceAfter() instead.
* @param [filter] {String|Function} to filter the event.
* Use a String if you want to filter DOM-events by a `selector`
* Use a function if you want to filter by any other means. If the function returns a trully value, the
* subscriber gets invoked. The function gets the `eventobject` as its only argument and the context is
* the subscriber.
* @param [prepend=false] {Boolean} whether the subscriber should be the first in the list of after-subscribers.
* @return {Object} handler with a `detach()`-method which can be used to detach the subscriber
* @since 0.0.1
*/
onceAfter: function(customEvent, callback, context, filter, prepend) {
var instance = this,
handler, wrapperFn;
console.log(NAME, 'add onceAfter subscriber to: '+customEvent);
wrapperFn = function(e) {
// CAUTIOUS: removeing the handler right now would lead into a mismatch of the dispatcher
// who loops through the array of subscribers!
// therefore, we must remove once the eventcycle has finished --> we detach by setting it
// at the end of the global-eventstack:
// yet there still is a change that the event is called multiple times BEFORE it
// will reach the defined `setTimeout` --> to avoid multiple invocations, handler is
// extended with the property `_detached`
handler._detached || callback.call(this, e);
handler._detached = true;
setTimeout(function() {handler.detach();}, 0);
};
handler = instance._addMultiSubs(false, customEvent, wrapperFn, context, filter, prepend);
return handler;
},
/**
* Subscribes to a customEvent. The callback will be executed `before` the defaultFn.
* The subscriber will be automaticly removed once the callback executed the first time.
* No need to `detach()` (unless you want to undescribe before the first event)
*
* @static
* @method onceBefore
* @param customEvent {String|Array} the custom-event (or Array of events) to subscribe to. CustomEvents should
* have the syntax: `emitterName:eventName`. Wildcard `*` may be used for both `emitterName` as well as `eventName`.
* If `emitterName` is not defined, `UI` is assumed.
* @param callback {Function} subscriber:will be invoked when the event occurs. An `eventobject` will be passed
* as its only argument.
* @param [context] {Object} the instance that subscribes to the event.
* any object can passed through, even those are not extended with event-listener methods.
* Note: Objects who are extended with listener-methods should use instance.onceBefore() instead.
* @param [filter] {String|Function} to filter the event.
* Use a String if you want to filter DOM-events by a `selector`
* Use a function if you want to filter by any other means. If the function returns a trully value, the
* subscriber gets invoked. The function gets the `eventobject` as its only argument and the context is
* the subscriber.
* @param [prepend=false] {Boolean} whether the subscriber should be the first in the list of before-subscribers.
* @return {Object} handler with a `detach()`-method which can be used to detach the subscriber
* @since 0.0.1
*/
onceBefore: function(customEvent, callback, context, filter, prepend) {
var instance = this,
handler, wrapperFn;
console.log(NAME, 'add onceBefore subscriber to: '+customEvent);
wrapperFn = function(e) {
// CAUTIOUS: removeing the handler right now would lead into a mismatch of the dispatcher
// who loops through the array of subscribers!
// therefore, we must remove once the eventcycle has finished --> we detach by setting it
// at the end of the global-eventstack.
// yet there still is a change that the event is called multiple times BEFORE it
// will reach the defined `setTimeout` --> to avoid multiple invocations, handler is
// extended with the property `_detached`
handler._detached || callback.call(this, e);
handler._detached = true;
setTimeout(function() {handler.detach();}, 0);
};
handler = instance._addMultiSubs(true, customEvent, wrapperFn, context, filter, prepend);
return handler;
},
/**
* Removes all event-definitions of an emitter, specified by its `emitterName`.
* When `emitterName` is not set, ALL event-definitions will be removed.
*
* @static
* @method undefAllEvents
* @param [emitterName] {String} name of the customEvent conform the syntax: `emitterName:eventName`
* @since 0.0.1
*/
undefAllEvents: function (emitterName) {
console.log(NAME, 'undefAllEvents');
var instance = this,
pattern;
if (emitterName) {
pattern = new RegExp('^'+emitterName+':');
instance._ce.each(
function(value, key) {
key.match(pattern) && (delete instance._ce[key]);
}
);
}
else {
instance._ce.each(
function(value, key) {
delete instance._ce[key];
}
);
}
},
/**
* Removes the event-definition of the specified customEvent.
*
* @static
* @method undefEvent
* @param customEvent {String} name of the customEvent conform the syntax: `emitterName:eventName`
* @since 0.0.1
*/
undefEvent: function (customEvent) {
console.log(NAME, 'undefEvent '+customEvent);
delete this._ce[customEvent];
},
/**
* unNotifies (unsubscribes) the notifier of the specified customEvent.
*
* @static
* @method unNotify
* @param customEvent {String} conform the syntax: `emitterName:eventName`.
* @since 0.0.1
*/
unNotify: function(customEvent) {
console.log(NAME, 'unNotify '+customEvent);
delete this._notifiers[customEvent];
},
/**
* unNotifies (unsubscribes) the detach-notifier of the specified customEvent.
*
* @static
* @method unNotifyDetach
* @param customEvent {String} conform the syntax: `emitterName:eventName`.
* @since 0.0.1
*/
unNotifyDetach: function(customEvent) {
console.log(NAME, 'unNotifyDetach '+customEvent);
delete this._detachNotifiers[customEvent];
},
//====================================================================================================
// private methods:
//====================================================================================================
/**
* Creates a subscriber to the specified customEvent. The customEvent must conform the syntax:
* `emitterName:eventName`. Wildcard `*` may be used for both `emitterName` as well as `eventName`
* If `emitterName` is not defined, `UI` is assumed.
*
* Examples of valid customevents:
*
* <ul>
* <li>'redmodel:save'</li>
* <li>'UI:tap'</li>
* <li>'tap' --> alias for 'UI:tap'</li>
* <li>'`*`:click' --> careful: will listen to both UIs and non-UI- click-events</li>
* <li>'redmodel:`*`'</li>
* <li>'`*`:`*`'</li>
* </ul>
*
* @static
* @method _addMultiSubs
* @param before {Boolean} whether the subscriber is a `before` subscriber. On falsy, an `after`-subscriber is assumed.
* @param customEvent {Array} Array of Strings. customEvent should conform the syntax: `emitterName:eventName`, wildcard `*`
* may be used for both `emitterName` as well as only `eventName`, in which case 'UI' will become the emitterName.
* @param callback {Function} subscriber to the event.
* @param listener {Object} Object that creates the subscriber (and will be listening by `listener.after(...)`)
* @param [filter] {String|Function} to filter the event.
* Use a String if you want to filter DOM-events by a `selector`
* Use a function if you want to filter by any other means. If the function returns a trully value, the
* subscriber gets invoked. The function gets the `eventobject` as its only argument and the context is
* the subscriber.
* @param [prepend=false] {Boolean} whether to make the subscriber the first in the list. By default it will pe appended.
* @return {Object} handler with a `detach()`-method which can be used to detach the subscriber
* @private
* @since 0.0.1
*/
_addMultiSubs: function(before, customEvent, callback, listener, filter, prepend) {
console.log(NAME, '_addMultiSubs');
var instance = this,
subscribers;
if ((typeof listener === 'string') || (typeof listener === 'function')) {
prepend = filter;
filter = listener;
listener = null;
}
else if (typeof listener === 'boolean') {
prepend = listener;
filter = null;
listener = null;
}
if ((typeof filter==='boolean') || (typeof filter===undefined) || (typeof filter===null)) {
// filter was not set, instead `prepend` is set at this position
prepend = filter;
filter = null;
}
if (!Array.isArray(customEvent)) {
return instance._addSubscriber(listener, before, customEvent, callback, filter, prepend);
}
subscribers = [];
customEvent.forEach(
function(ce) {
subscribers.push(instance._addSubscriber(listener, before, ce, callback, filter, prepend));
}
);
return {
detach: function() {
subscribers.each(
function(subscriber) {
subscriber.detach();
}
);
}
};
},
/**
* Creates a subscriber to the specified customEvent. The customEvent must conform the syntax:
* `emitterName:eventName`. Wildcard `*` may be used for both `emitterName` as well as `eventName`
* If `emitterName` is not defined, `UI` is assumed.
*
* Examples of valid customevents:
*
* <ul>
* <li>'redmodel:save'</li>
* <li>'UI:tap'</li>
* <li>'tap' --> alias for 'UI:tap'</li>
* <li>'`*`:click' --> careful: will listen to both UIs and non-UI- click-events</li>
* <li>'redmodel:`*`'</li>
* <li>'`*`:`*`'</li>
* </ul>
*
* @static
* @method _addSubscriber
* @param listener {Object} Object that creates the subscriber (and will be listening by `listener.after(...)`)
* @param before {Boolean} whether the subscriber is a `before` subscriber. On falsy, an `after`-subscriber is assumed.
* @param customEvent {String} conform the syntax: `emitterName:eventName`, wildcard `*` may be used for both
* `emitterName` as well as only `eventName`, in which case 'UI' will become the emitterName.
* @param callback {Function} subscriber to the event.
* @param [filter] {String|Function} to filter the event.
* Use a String if you want to filter DOM-events by a `selector`
* Use a function if you want to filter by any other means. If the function returns a trully value, the
* subscriber gets invoked. The function gets the `eventobject` as its only argument and the context is
* the subscriber.
* @param [prepend=false] {Boolean} whether to make the subscriber the first in the list. By default it will pe appended.
* @return {Object} handler with a `detach()`-method which can be used to detach the subscriber
* @private
* @since 0.0.1
*/
_addSubscriber: function(listener, before, customEvent, callback, filter, prepend) {
var instance = this,
allSubscribers = instance._subs,
extract = customEvent.match(REGEXP_WILDCARD_CUSTOMEVENT),
hashtable, item, notifier, customEventWildcardEventName, customEventWildcardEmitterName;
if (!extract) {
console.error(NAME, 'subscribe-error: eventname does not match pattern');
return;
}
item = {
o: listener || instance,
cb: callback,
f: filter
};
console.warn(item);
// if extract[1] is undefined, a simple customEvent is going to subscribe (without :)
// therefore: recomposite customEvent:
extract[1] || (customEvent='UI:'+customEvent);
// if extract[1] === 'this', then a listener to its own emitterName is supposed
if (extract[1]==='this') {
if (listener._emitterName) {
customEvent = listener._emitterName+':'+extract[2];
item.s = true; // s --> self
}
else {
console.error(NAME, 'subscribe-error: "this" cannot be detemined because the object is no emitter');
return;
}
}
allSubscribers[customEvent] || (allSubscribers[customEvent]={});
if (before) {
allSubscribers[customEvent].b || (allSubscribers[customEvent].b=[]);
}
else {
allSubscribers[customEvent].a || (allSubscribers[customEvent].a=[]);
}
hashtable = allSubscribers[customEvent][before ? 'b' : 'a'];
// we need to be able to process an array of customevents
// in case of a defined subscription (no wildcard), we should look for notifiers
if ((extract[1]!=='*') && (extract[2]!=='*')) {
// before subscribing: we might need to activate notifiers --> with defined eventName should also be cleaned up:
notifier = instance._notifiers[customEvent];
if (notifier) {
notifier.cb.call(notifier.o, customEvent, item);
if (notifier.r) {
delete instance._notifiers[customEvent];
}
}
// check the same for wildcard eventName:
customEventWildcardEventName = customEvent.replace(REGEXP_EVENTNAME_WITH_SEMICOLON, ':*');
if ((customEventWildcardEventName !== customEvent) && (notifier=instance._notifiers[customEventWildcardEventName])) {
notifier.cb.call(notifier.o, customEvent, item);
if (notifier.r) {
delete instance._notifiers[customEvent];
}
}
// check the same for wildcard emitterName:
customEventWildcardEmitterName = customEvent.replace(REGEXP_EMITTERNAME_WITH_SEMICOLON, '*:');
if ((customEventWildcardEmitterName !== customEvent) && (notifier=instance._notifiers[customEventWildcardEmitterName])) {
notifier.cb.call(notifier.o, customEvent, item);
if (notifier.r) {
delete instance._notifiers[customEvent];
}
}
// check the same for wildcard emitterName and eventName:
if ((WILDCARD_WILDCARD !== customEvent) && (notifier=instance._notifiers[WILDCARD_WILDCARD])) {
notifier.cb.call(notifier.o, customEvent, item);
if (notifier.r) {
delete instance._notifiers[customEvent];
}
}
}
console.log(NAME, '_addSubscriber to customEvent: '+customEvent);
prepend ? hashtable.unshift(item) : hashtable.push(item);
return {
detach: function() {
instance._removeSubscriber(listener, before, customEvent, callback);
}
};
},
/**
* Emits the event `eventName` on behalf of `emitter`, which becomes e.target in the eventobject.
* During this process, all subscribers and the defaultFn/preventedFn get an eventobject passed through.
* The eventobject is created with at least these properties:
*
* <ul>
* <li>e.target --> source that triggered the event (instance or DOM-node), specified by `emitter`</li>
* <li>e.type --> eventName</li>
* <li>e.emitter --> emitterName</li>
* <li>e.status --> status-information:
* <ul>
* <li>e.status.ok --> `true|false` whether the event got executed (not halted or defaultPrevented)</li>
* <li>e.status.defaultFn (optional) --> `true` if any defaultFn got invoked</li>
* <li>e.status.preventedFn (optional) --> `true` if any preventedFn got invoked</li>
* <li>e.status.halted (optional) --> `reason|true` if the event got halted and optional the why</li>
* <li>e.status.defaultPrevented (optional) --> `reason|true` if the event got defaultPrevented and optional the why</li>
* </ul>
* </li>
* </ul>
*
* The optional `payload` is merged into the eventobject and could be used by the subscribers and the defaultFn/preventedFn.
* If payload.silent is set true, the subscribers are not getting invoked: only the defaultFn.
*
* The eventobject also has these methods:
*
* <ul>
* <li>e.halt() --> stops immediate all actions: no mer subscribers are invoked, no defaultFn/preventedFn</li>
* <li>e.preventDefault() --> instead of invoking defaultFn, preventedFn will be invoked. No aftersubscribers</li>
* </ul>
*
* <ul>
* <li>First, before-subscribers are invoked: this is the place where you might call `e.halt()` or `a.preventDefault()`</li>
* <li>Next, defaultFn or preventedFn gets invoked, depending on whether e.halt() or a.preventDefault() has been called</li>
* <li>Finally, after-subscribers get invoked (unless e.halt() or a.preventDefault() has been called)</li>
* <ul>
*
* @static
* @method emit
* @param [emitter] {Object} instance that emits the events
* @param customEvent {String} Full customEvent conform syntax `emitterName:eventName`.
* `emitterName` is available as **e.emitter**, `eventName` as **e.type**.
* @param payload {Object} extra payload to be added to the event-object
* @param [beforeSubscribers] {Array} array of functions to act as beforesubscribers. <b>should not be used</b> other than
* by any submodule like `event-dom`. If used, than this list of subscribers gets invoked instead
* of the subscribers that actually subscribed to the event.
* @param [afterSubscribers] {Array} array of functions to act as afterSubscribers. <b>should not be used</b> other than
* by any submodule like `event-dom`. If used, than this list of subscribers gets invoked instead
* of the subscribers that actually subscribed to the event.
* @param [preProcessor] {Function} if passed, this function will be invoked before every single subscriber
* It is meant to manipulate the eventobject, something that `event-dom` needs to do
* This function expects 2 arguments: `subscriber` and `eventobject`.
* <b>should not be used</b> other than by any submodule like `event-dom`.
* @param [keepPayload=false] {Boolean} whether `payload` should be used as the ventobject instead of creating a new
* eventobject and merge payload. <b>should not be used</b> other than by any submodule like `event-dom`.
* @return {Object|undefined} eventobject or undefined when the event was halted or preventDefaulted.
* @since 0.0.1
*/
_emit: function (emitter, customEvent, payload, beforeSubscribers, afterSubscribers, preProcessor, keepPayload) {
// NOTE: emit() needs to be synchronous! otherwise we wouldn't be able
// to preventDefault DOM-events in time.
var instance = this,
allCustomEvents = instance._ce,
allSubscribers = instance._subs,
customEventDefinition, extract, emitterName, eventName, subs, wildcard_named_subs,
named_wildcard_subs, wildcard_wildcard_subs, e, invokeSubs, key, propDescriptor;
(customEvent.indexOf(':') !== -1) || (customEvent = emitter._emitterName+':'+customEvent);
console.log(NAME, 'customEvent.emit: '+customEvent);
extract = customEvent.match(REGEXP_CUSTOMEVENT);
if (!extract) {
console.error(NAME, 'defined emit-event does not match pattern');
return;
}
emitterName = extract[1];
eventName = extract[2];
customEventDefinition = allCustomEvents[customEvent];
subs = allSubscribers[customEvent];
wildcard_named_subs = allSubscribers['*:'+eventName];
named_wildcard_subs = allSubscribers[emitterName+':*'];
wildcard_wildcard_subs = allSubscribers['*:*'];
if (keepPayload) {
e = payload;
}
else {
e = Object.create(instance._defaultEventObj);
// e.target = (payload && payload.target) || emitter; // make it possible to force a specific e.target
e.target = emitter;
e.type = eventName;
e.emitter = emitterName;
e.status = {};
if (customEventDefinition) {
e._unPreventable = customEventDefinition.unPreventable;
e._unHaltable = customEventDefinition.unHaltable;
customEventDefinition.unSilencable && (e.status.unSilencable = true);
}
if (payload) {
// e.merge(payload); is not enough --> DOM-eventobject has many properties that are not "own"-properties
for (key in payload) {
if (!(key in e)) {
propDescriptor = Object.getOwnPropertyDescriptor(payload, key);
if (!propDescriptor || !propDescriptor.writable) {
e[key] = payload[key];
}
else {
Object.defineProperty(e, key, propDescriptor);
}
}
}
}
if (e.status.unSilencable && e.silent) {
console.warn(NAME, ' event '+e.emitter+':'+e.type+' cannot made silent: this customEvent is defined as unSilencable');
e.silent = false;
}
}
if (beforeSubscribers) {
instance._invokeSubs(e, false, true, preProcessor, {b: beforeSubscribers});
}
else {
invokeSubs = instance._invokeSubs.bind(instance, e, true, true, false);
[subs, named_wildcard_subs, wildcard_named_subs, wildcard_wildcard_subs].forEach(invokeSubs);
}
e.status.ok = !e.status.halted && !e.status.defaultPrevented;
// in case any subscriber changed e.target inside its filter (event-dom does this),
// then we reset e.target to its original. But only if e._noResetSourceTarget is undefined:
// (e._noResetSourceTarget can be used to supress this behaviour --> dragdrop uses this)
e.sourceTarget && !e._noResetSourceTarget && (e.target=e.sourceTarget);
if (customEventDefinition && !e.status.halted) {
// now invoke defFn
e.returnValue = (e.status.defaultPrevented || e.status.defaultPreventedContinue) ?
(customEventDefinition.preventedFn && (e.status.preventedFn=true) && customEventDefinition.preventedFn.call(e.target, e)) :
(customEventDefinition.defaultFn && (e.status.defaultFn=true) && customEventDefinition.defaultFn.call(e.target, e));
}
if (e.status.ok) {
if (afterSubscribers) {
instance._invokeSubs(e, false, false, preProcessor, {a: afterSubscribers});
}
else {
invokeSubs = instance._invokeSubs.bind(instance, e, true, false, false);
[subs, named_wildcard_subs, wildcard_named_subs, wildcard_wildcard_subs].forEach(invokeSubs);
}
}
return e;
},
/**
* Does the actual invocation of a subscriber.
*
* @method _invokeSubs
* @param e {Object} event-object
* @param [checkFilter] {Boolean}
* @param [before] {Boolean} whether it concerns before subscribers
* @param [checkFilter] {Boolean}
* @param subscribers {Array} contains subscribers (objects) with these members:
* <ul>
* <li>subscriber.o {Object} context of the callback</li>
* <li>subscriber.cb {Function} callback to be invoked</li>
* <li>subscriber.f {Function} filter to be applied</li>
* <li>subscriber.t {DOM-node} target for the specific selector, which will be set as e.target
* only when event-dom is active and there are filter-selectors</li>
* <li>subscriber.n {DOM-node} highest dom-node that acts as the container for delegation.
* only when event-dom is active and there are filter-selectors</li>
* <li>subscriber.s {Boolean} true when the subscription was set to itself by using "this:eventName"</li>
* </ul>
* @private
* @since 0.0.1
*/
_invokeSubs: function (e, checkFilter, before, preProcessor, subscribers) { // subscribers, plural
console.log(NAME, '_invokeSubs');
var subs, passesThis, passesFilter;
if (subscribers && !e.status.halted && !e.silent) {
subs = before ? subscribers.b : subscribers.a;
subs && subs.some(function(subscriber) {
console.log(NAME, '_invokeSubs checking invokation for single subscriber');
if (preProcessor && preProcessor(subscriber, e)) {
return true;
}
// check: does it need to be itself because of subscribing through 'this'
passesThis = (!subscriber.s || (subscriber.o===e.target));
// check: does it pass the filter
passesFilter = (!checkFilter || !subscriber.f || subscriber.f.call(subscriber.o, e));
if (passesThis && passesFilter) {
// finally: invoke subscriber
console.log(NAME, '_invokeSubs is going to invoke subscriber');
subscriber.cb.call(subscriber.o, e);
}
if (e.status.unSilencable && e.silent) {
console.warn(NAME, ' event '+e.emitter+':'+e.type+' cannot made silent: this customEvent is defined as unSilencable');
e.silent = false;
}
return e.silent || (before && e.status.halted); // remember to check whether it was halted for any reason
});
}
},
/**
* Removes a subscriber from the specified customEvent. The customEvent must conform the syntax:
* `emitterName:eventName`.
*
* @static
* @method _removeSubscriber
* @param listener {Object} Object that creates the subscriber (and will be listening by `listener.after(...)`)
* @param before {Boolean} whether the subscriber is a `before` subscriber. On falsy, an `after`-subscriber is assumed.
* @param customEvent {String} conform the syntax: `emitterName:eventName`, wildcard `*` may be used for both
* `emitterName` as well as only `eventName`, in which case 'UI' will become the emmiterName.
* @param [callback] {Function} subscriber to the event, when not set, all subscribers of the listener to this customEvent
* will be removed.
* @private
* @since 0.0.1
*/
_removeSubscriber: function(listener, before, customEvent, callback) {
console.log('_removeSubscriber: '+customEvent);
var instance = this,
eventSubscribers = instance._subs[customEvent],
hashtable = eventSubscribers && eventSubscribers[before ? 'b' : 'a'],
i, subscriber, beforeUsed, afterUsed, extract, detachNotifier, customEventWildcardEventName;
if (hashtable) {
// unfortunatly we cannot search by reference, because the array has composed objects
// also: can't use native Array.forEach: removing items within its callback change the array
// during runtime, making it to skip the next item of the one that's being removed
for (i=0; i<hashtable.length; ++i) {
console.log(NAME, '_removeSubscriber for single subscriber');
subscriber = hashtable[i];
if ((subscriber.o===(listener || instance)) && (!callback || (subscriber.cb===callback))) {
console.log('removing subscriber');
hashtable.splice(i--, 1);
}
}
}
// After removal subscriber: check whether both eventSubscribers.a and eventSubscribers.b are empty
// if so, remove the member from Event._subs to cleanup memory
if (eventSubscribers) {
beforeUsed = eventSubscribers.b && (eventSubscribers.b.length>0);
afterUsed = eventSubscribers.a && (eventSubscribers.a.length>0);
if (!beforeUsed && !afterUsed) {
delete instance._subs[customEvent];
}
}
extract = customEvent.match(REGEXP_CUSTOMEVENT);
// in case of a defined subscription (no wildcard),
// we need to inform any detachNotifier of the unsubscription:
if (extract && ((extract[1]!=='*') && (extract[2]!=='*'))) {
detachNotifier = instance._detachNotifiers[customEvent];
if (detachNotifier) {
detachNotifier.cb.call(detachNotifier.o, customEvent);
if (detachNotifier.r) {
delete instance._detachNotifiers[customEvent];
}
}
// check the same for wildcard eventName:
customEventWildcardEventName = customEvent.replace(REGEXP_EVENTNAME_WITH_SEMICOLON, ':*');
if ((customEventWildcardEventName !== customEvent) && (detachNotifier=instance._detachNotifiers[customEventWildcardEventName])) {
detachNotifier.cb.call(detachNotifier.o, customEvent);
if (detachNotifier.r) {
delete instance._detachNotifiers[customEvent];
}
}
}
},
/**
* Removes subscribers from the multiple customevents. The customEvent must conform the syntax:
* `emitterName:eventName`. Wildcard `*` may be used for both `emitterName` as well as `eventName`
* If `emitterName` is not defined, `UI` is assumed.
*
* Examples of valid customevents:
*
* <ul>
* <li>'redmodel:save'</li>
* <li>'UI:tap'</li>
* <li>'tap' --> alias for 'UI:tap'</li>
* <li>'`*`:click' --> careful: will listen to both UIs and non-UI- click-events</li>
* <li>'redmodel:`*`'</li>
* <li>'`*`:`*`'</li>
* </ul>
*
* @static
* @method _removeSubscriber
* @param listener {Object} Object that creates the subscriber (and will be listening by `listener.after(...)`)
* @param customEvent {String} conform the syntax: `emitterName:eventName`, wildcard `*` may be used for both
* `emitterName` as well as only `eventName`, in which case 'UI' will become the emmiterName.
* @private
* @since 0.0.1
*/
_removeSubscribers: function(listener, customEvent) {
console.log('_removeSubscribers: '+customEvent);
var instance = this,
emitterName, eventName,
extract = customEvent.match(REGEXP_WILDCARD_CUSTOMEVENT);
if (!extract) {
console.error(NAME, '_removeSubscribers-error: customEvent '+customEvent+' does not match pattern');
return;
}
emitterName = extract[1] || 'UI';
eventName = extract[2];
if ((emitterName!=='*') && (eventName!=='*')) {
instance._removeSubscriber(listener, true, customEvent);
instance._removeSubscriber(listener, false, customEvent);
}
else {
// wildcard, we need to look at all the members of Event._subs
instance._subs.each(
function(value, key) {
var localExtract = key.match(REGEXP_WILDCARD_CUSTOMEVENT),
emitterMatch = (emitterName==='*') || (emitterName===localExtract[1]),
eventMatch = (eventName==='*') || (eventName===localExtract[2]);
if (emitterMatch && eventMatch) {
instance._removeSubscriber(listener, true, key);
instance._removeSubscriber(listener, false, key);
}
}
);
}
},
/**
* Adds a property to the default eventobject's prototype which passes through all eventcycles.
* Goes through Object.defineProperty with configurable, enumerable and writable
* all set to false.
*
* @method _setEventObjProperty
* @param property {String} event-object
* @param value {Any}
* @chainable
* @private
* @since 0.0.1
*/
_setEventObjProperty: function (property, value) {
console.log(NAME, '_setEventObjProperty');
Object.protectedProp(this._defaultEventObj, property, value);
return this;
}
};
/**
* Objecthash containing all defined custom-events
* which has a structure like this:
*
* _ce = {
* 'UI:tap': {
* preventable: true,
* defaultFn: function(){...},
* preventedFn: function(){...}
* },
* 'redmodel:save': {
* preventable: true,
* defaultFn: function(){...},
* preventedFn: function(){...}
* }
* }
*
* @property _ce
* @default {}
* @type Object
* @private
* @since 0.0.1
*/
Object.defineProperty(Event, '_ce', {
configurable: false,
enumerable: false,
writable: false,
value: {} // `writable` is false means we cannot chance the value-reference, but we can change {}'s properties itself
});
/**
* Objecthash containing all defined before and after subscribers
* which has a structure like this (`b` represents `before` and `a` represents `after`)
* Every item that gets in the array consist by itself of 3 properties:
* subscriberitem = {
* o: listener,
* cb: callbackFn(e),
* f: filter
* };
*
* _subs = {
* 'UI:tap': {
* b: [
* item,
* item
* ],
* a: [
* item,
* item
* ]
* },
* '*:click': {
* b: [
* item,
* item
* ],
* a: [
* item,
* item
* ]
* },
* 'redmodel:save': {
* b: [
* item,
* item
* ],
* a: [
* item,
* item
* ]
* }
* }
*
* @property _ce
* @default {}
* @type Object
* @private
* @since 0.0.1
*/
Object.protectedProp(Event, '_subs', {});
/**
* Object that acts as the prototype of the eventobject.
* To add more methods, you can use `_setEventObjProperty`
*
* @property _defaultEventObj
* @default {
* halt: function(),
* preventDefault: function()
* }
* @type Object
* @private
* @since 0.0.1
*/
Object.protectedProp(Event, '_defaultEventObj', {});
/**
* Objecthash containing all detach-notifiers, keyed by customEvent name.
* This list is maintained by `notifyDetach` and `unNotifyDetach`
*
* _detachNotifiers = {
* 'UI:tap': {
* cb:function() {}
* o: {} // context
* },
* 'redmodel:*': {
* cb:function() {}
* o: {} // context
* },
* 'bluemodel:save': {
* cb:function() {}
* o: {} // context
* }
* }
*
* @property _detachNotifiers
* @default {}
* @type Object
* @private
* @since 0.0.1
*/
Object.protectedProp(Event, '_detachNotifiers', {});
/**
* Objecthash containing all notifiers, keyed by customEvent name.
* This list is maintained by `notify` and `unNotify`
*
* _notifiers = {
* 'UI:tap': {
* cb:function() {}
* o: {} // context
* },
* 'redmodel:*': {
* cb:function() {}
* o: {} // context
* },
* 'bluemodel:save': {
* cb:function() {}
* o: {} // context
* }
* }
*
* @property _notifiers
* @default {}
* @type Object
* @private
* @since 0.0.1
*/
Object.protectedProp(Event, '_notifiers', {});
Event._setEventObjProperty('halt', function(reason) {this.status.ok || this._unHaltable || (this.status.halted = (reason || true));})
._setEventObjProperty('preventDefault', function(reason) {this.status.ok || this._unPreventable || (this.status.defaultPrevented = (reason || true));})
._setEventObjProperty('preventDefaultContinue', function(reason) {this.status.ok || this._unPreventable || (this.status.defaultPreventedContinue = (reason || true));});
return Event;
}));
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"js-ext/extra/hashmap.js":12,"js-ext/lib/object.js":13,"polyfill/polyfill-base.js":25}],8:[function(require,module,exports){
"use strict";
/**
* Extends the Event-instance by adding the method `Emitter` to it.
* The `Emitter-method` returns an object that should be merged into any Class-instance or object you
* want to extend with the emit-methods, so the appropriate methods can be invoked on the instance.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
*
* Should be called using the provided `extend`-method like this:
* @example
* var Event = require('event');<br>
*
* @module event
* @submodule event-emitter
* @class Event.Emitter
* @since 0.0.1
*/
var NAME = '[event-emitter]: ',
REGEXP_EMITTER = /^(\w|-|#)+$/,
Event = require('./event-base.js');
Event.Emitter = function(emitterName) {
var composeCustomevent = function(eventName) {
return emitterName+':'+eventName;
},
newEmitter;
if (!REGEXP_EMITTER.test(emitterName)) {
console.error(NAME, 'Emitter invoked with invalid argument: you must specify a valid emitterName');
return;
}
newEmitter = {
/**
* Defines a CustomEvent. If the eventtype already exists, it will not be overridden,
* unless you force to assign with `.forceAssign()`
*
* The returned object comes with 4 methods which can be invoked chainable:
*
* <ul>
* <li>defaultFn() --> the default-function of the event</li>
* <li>preventedFn() --> the function that should be invoked when the event is defaultPrevented</li>
* <li>forceAssign() --> overrides any previous definition</li>
* <li>unHaltable() --> makes the customEvent cannot be halted</li>
* <li>unPreventable() --> makes the customEvent's defaultFn cannot be prevented</li>
* <li>unSilencable() --> makes that emitters cannot make this event to perform silently (using e.silent)</li>
* </ul>
*
* @method defineEvent
* @param eventName {String} name of the customEvent, without `emitterName`.
* The final event that will be created has the syntax: `emitterName:eventName`,
* where `emitterName:` is automaticly prepended.
* @return {Object} with extra methods that can be chained:
* <ul>
* <li>unPreventable() --> makes the customEvent's defaultFn cannot be prevented</li>
* <li>forceAssign() --> overrides any previous definition</li>
* <li>defaultFn() --> the default-function of the event</li>
* <li>preventedFn() --> the function that should be invoked when the event is defaultPrevented</li>
* </ul>
* @since 0.0.1
*/
defineEvent: function (eventName) {
return Event.defineEvent(composeCustomevent(eventName));
},
/**
* Emits the event `eventName` on behalf of the instance holding this method.
*
* @method emit
* @param eventName {String} name of the event to be sent (available as e.type)
* you could pass a customEvent here 'emitterName:eventName', which would
* overrule the `instance-emitterName`
* @param payload {Object} extra payload to be added to the event-object
* @return {Promise}
* <ul>
* <li>on success: returnValue {Any} of the defaultFn</li>
* <li>on error: reason {Any} Either: description 'event was halted', 'event was defaultPrevented' or the returnvalue of the preventedFn</li>
* </ul>
* @since 0.0.1
*/
emit: function(eventName, payload) {
return Event.emit(this, eventName, payload);
},
/**
* Removes all event-definitions of the instance holding this method.
*
* @method undefAllEvents
* @since 0.0.1
*/
undefAllEvents: function () {
Event.undefEvent(emitterName);
},
/**
* Removes the event-definition of the specified customEvent.
*
* @method undefEvent
* @param eventName {String} name of the customEvent, without `emitterName`.
* The calculated customEvent which will be undefined, will have the syntax: `emitterName:eventName`.
* where `emitterName:` is automaticly prepended.
* @since 0.0.1
*/
undefEvent: function (eventName) {
Event.undefEvent(composeCustomevent(eventName));
}
};
// register the emittername:
Event.defineEmitter(newEmitter, emitterName);
return newEmitter;
};
module.exports = Event;
},{"./event-base.js":7}],9:[function(require,module,exports){
"use strict";
/**
* Extends the Event-instance by adding the object `Listener` to it.
* The returned object should be merged into any Class-instance or object you want to
* extend with the listener-methods, so the appropriate methods can be invoked on the instance.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
*
* Should be called using the provided `extend`-method like this:
* @example
* var Event = require('event');<br>
*
* @module event
* @submodule event-listener
* @class Event.Listener
* @since 0.0.1
*/
require('js-ext/lib/object.js');
var Event = require('./event-base.js'),
Classes = require("js-ext/extra/classes.js"),
callbackFn, ClassListener;
Event.Listener = {
/**
* Subscribes to a customEvent on behalf of the object who calls this method.
* The callback will be executed `after` the defaultFn.
*
* @method after
* @param customEvent {String|Array} the custom-event (or Array of events) to subscribe to. CustomEvents should
* have the syntax: `emitterName:eventName`. Wildcard `*` may be used for both `emitterName` as well as `eventName`.
* If `emitterName` is not defined, `UI` is assumed.
* @param callback {Function} subscriber:will be invoked when the event occurs. An `eventobject` will be passed
* as its only argument.
* @param [filter] {String|Function} to filter the event.
* Use a String if you want to filter DOM-events by a `selector`
* Use a function if you want to filter by any other means. If the function returns a trully value, the
* subscriber gets invoked. The function gets the `eventobject` as its only argument and the context is
* the subscriber.
* @param [prepend=false] {Boolean} whether the subscriber should be the first in the list of after-subscribers.
* @return {Object} handler with a `detach()`-method which can be used to detach the subscriber
* @since 0.0.1
*/
after: function (customEvent, callback, filter, prepend) {
return Event.after(customEvent, callback, this, filter, prepend);
},
/**
* Subscribes to a customEvent on behalf of the object who calls this method.
* The callback will be executed `before` the defaultFn.
*
* @method before
* @param customEvent {String|Array} the custom-event (or Array of events) to subscribe to. CustomEvents should
* have the syntax: `emitterName:eventName`. Wildcard `*` may be used for both `emitterName` as well as `eventName`.
* If `emitterName` is not defined, `UI` is assumed.
* @param callback {Function} subscriber:will be invoked when the event occurs. An `eventobject` will be passed
* as its only argument.
* @param [filter] {String|Function} to filter the event.
* Use a String if you want to filter DOM-events by a `selector`
* Use a function if you want to filter by any other means. If the function returns a trully value, the
* subscriber gets invoked. The function gets the `eventobject` as its only argument and the context is
* the subscriber.
* @param [prepend=false] {Boolean} whether the subscriber should be the first in the list of before-subscribers.
* @return {Object} handler with a `detach()`-method which can be used to detach the subscriber
* @since 0.0.1
*/
before: function (customEvent, callback, filter, prepend) {
return Event.before(customEvent, callback, this, filter, prepend);
},
/**
* Detaches (unsubscribes) the listener from the specified customEvent,
* on behalf of the object who calls this method.
*
* @method detach
* @param customEvent {String} conform the syntax: `emitterName:eventName`, wildcard `*` may be used for both
* `emitterName` as well as only `eventName`, in which case 'UI' will become the emitterName.
* @since 0.0.1
*/
detach: function(customEvent) {
Event.detach(this, customEvent);
},
/**
* Detaches (unsubscribes) the listener from all customevents,
* on behalf of the object who calls this method.
*
* @method detachAll
* @since 0.0.1
*/
detachAll: function() {
Event.detachAll(this);
},
/**
* Subscribes to a customEvent on behalf of the object who calls this method.
* The callback will be executed `after` the defaultFn.
* The subscriber will be automaticly removed once the callback executed the first time.
* No need to `detach()` (unless you want to undescribe before the first event)
*
* @method onceAfter
* @param customEvent {String|Array} the custom-event (or Array of events) to subscribe to. CustomEvents should
* have the syntax: `emitterName:eventName`. Wildcard `*` may be used for both `emitterName` as well as `eventName`.
* If `emitterName` is not defined, `UI` is assumed.
* @param callback {Function} subscriber:will be invoked when the event occurs. An `eventobject` will be passed
* as its only argument.
* @param [filter] {String|Function} to filter the event.
* Use a String if you want to filter DOM-events by a `selector`
* Use a function if you want to filter by any other means. If the function returns a trully value, the
* subscriber gets invoked. The function gets the `eventobject` as its only argument and the context is
* the subscriber.
* @param [prepend=false] {Boolean} whether the subscriber should be the first in the list of after-subscribers.
* @return {Object} handler with a `detach()`-method which can be used to detach the subscriber
* @since 0.0.1
*/
onceAfter: function (customEvent, callback, filter, prepend) {
return Event.onceAfter(customEvent, callback, this, filter, prepend);
},
/**
* Subscribes to a customEvent on behalf of the object who calls this method.
* The callback will be executed `before` the defaultFn.
* The subscriber will be automaticly removed once the callback executed the first time.
* No need to `detach()` (unless you want to undescribe before the first event)
*
* @method onceBefore
* @param customEvent {String|Array} the custom-event (or Array of events) to subscribe to. CustomEvents should
* have the syntax: `emitterName:eventName`. Wildcard `*` may be used for both `emitterName` as well as `eventName`.
* If `emitterName` is not defined, `UI` is assumed.
* @param callback {Function} subscriber:will be invoked when the event occurs. An `eventobject` will be passed
* as its only argument.
* @param [filter] {String|Function} to filter the event.
* Use a String if you want to filter DOM-events by a `selector`
* Use a function if you want to filter by any other means. If the function returns a trully value, the
* subscriber gets invoked. The function gets the `eventobject` as its only argument and the context is
* the subscriber.
* @param [prepend=false] {Boolean} whether the subscriber should be the first in the list of before-subscribers.
* @return {Object} handler with a `detach()`-method which can be used to detach the subscriber
* @since 0.0.1
*/
onceBefore: function (customEvent, callback, filter, prepend) {
return Event.onceBefore(customEvent, callback, this, filter, prepend);
}
};
callbackFn = function(callback, e) {
var instance = this,
eTarget = e.target,
accept;
accept = (eTarget===instance) || (eTarget.vnode && instance.vnode && instance.contains(eTarget));
accept && callback.call(instance, e);
};
Event._CE_listener = ClassListener = {
/**
* Is automaticly available for Classes.
* Subscribes to a customEvent on behalf of the class-instance and will only
* be executed when the emitter is the instance itself.
*
* The callback will be executed `after` the defaultFn.
*
* @method selfAfter
* @param customEvent {String|Array} the custom-event (or Array of events) to subscribe to. CustomEvents should
* have the syntax: `emitterName:eventName`. Wildcard `*` may be used for both `emitterName` as well as `eventName`.
* If `emitterName` is not defined, `UI` is assumed.
* @param callback {Function} subscriber:will be invoked when the event occurs. An `eventobject` will be passed
* as its only argument.
* @param [filter] {String|Function} to filter the event.
* Use a String if you want to filter DOM-events by a `selector`
* Use a function if you want to filter by any other means. If the function returns a trully value, the
* subscriber gets invoked. The function gets the `eventobject` as its only argument and the context is
* the subscriber.
* @param [prepend=false] {Boolean} whether the subscriber should be the first in the list of after-subscribers.
* @return {Object} handler with a `detach()`-method which can be used to detach the subscriber
* @since 0.0.1
*/
selfAfter: function (customEvent, callback, filter, prepend) {
return Event.after(customEvent, callbackFn.bind(this, callback), this, filter, prepend);
},
/**
* Is automaticly available for Classes.
* Subscribes to a customEvent on behalf of the class-instance and will only
* be executed when the emitter is the instance itself.
*
* The callback will be executed `before` the defaultFn.
*
* @method selfBefore
* @param customEvent {String|Array} the custom-event (or Array of events) to subscribe to. CustomEvents should
* have the syntax: `emitterName:eventName`. Wildcard `*` may be used for both `emitterName` as well as `eventName`.
* If `emitterName` is not defined, `UI` is assumed.
* @param callback {Function} subscriber:will be invoked when the event occurs. An `eventobject` will be passed
* as its only argument.
* @param [filter] {String|Function} to filter the event.
* Use a String if you want to filter DOM-events by a `selector`
* Use a function if you want to filter by any other means. If the function returns a trully value, the
* subscriber gets invoked. The function gets the `eventobject` as its only argument and the context is
* the subscriber.
* @param [prepend=false] {Boolean} whether the subscriber should be the first in the list of before-subscribers.
* @return {Object} handler with a `detach()`-method which can be used to detach the subscriber
* @since 0.0.1
*/
selfBefore: function (customEvent, callback, filter, prepend) {
return Event.before(customEvent, callbackFn.bind(this, callback), this, filter, prepend);
},
/**
* Is automaticly available for Classes.
* Subscribes to a customEvent on behalf of the class-instance and will only
* be executed when the emitter is the instance itself.
*
* The callback will be executed `after` the defaultFn.
* The subscriber will be automaticly removed once the callback executed the first time.
* No need to `detach()` (unless you want to undescribe before the first event)
*
* @method selfOnceAfter
* @param customEvent {String|Array} the custom-event (or Array of events) to subscribe to. CustomEvents should
* have the syntax: `emitterName:eventName`. Wildcard `*` may be used for both `emitterName` as well as `eventName`.
* If `emitterName` is not defined, `UI` is assumed.
* @param callback {Function} subscriber:will be invoked when the event occurs. An `eventobject` will be passed
* as its only argument.
* @param [filter] {String|Function} to filter the event.
* Use a String if you want to filter DOM-events by a `selector`
* Use a function if you want to filter by any other means. If the function returns a trully value, the
* subscriber gets invoked. The function gets the `eventobject` as its only argument and the context is
* the subscriber.
* @param [prepend=false] {Boolean} whether the subscriber should be the first in the list of after-subscribers.
* @return {Object} handler with a `detach()`-method which can be used to detach the subscriber
* @since 0.0.1
*/
selfOnceAfter: function (customEvent, callback, filter, prepend) {
return Event.onceAfter(customEvent, callbackFn.bind(this, callback), this, filter, prepend);
},
/**
* Is automaticly available for Classes.
* Subscribes to a customEvent on behalf of the class-instance and will only
* be executed when the emitter is the instance itself.
*
* The callback will be executed `before` the defaultFn.
* The subscriber will be automaticly removed once the callback executed the first time.
* No need to `detach()` (unless you want to undescribe before the first event)
*
* @method selfOnceBefore
* @param customEvent {String|Array} the custom-event (or Array of events) to subscribe to. CustomEvents should
* have the syntax: `emitterName:eventName`. Wildcard `*` may be used for both `emitterName` as well as `eventName`.
* If `emitterName` is not defined, `UI` is assumed.
* @param callback {Function} subscriber:will be invoked when the event occurs. An `eventobject` will be passed
* as its only argument.
* @param [filter] {String|Function} to filter the event.
* Use a String if you want to filter DOM-events by a `selector`
* Use a function if you want to filter by any other means. If the function returns a trully value, the
* subscriber gets invoked. The function gets the `eventobject` as its only argument and the context is
* the subscriber.
* @param [prepend=false] {Boolean} whether the subscriber should be the first in the list of before-subscribers.
* @return {Object} handler with a `detach()`-method which can be used to detach the subscriber
* @since 0.0.1
*/
selfOnceBefore: function (customEvent, callback, filter, prepend) {
return Event.onceBefore(customEvent, callbackFn.bind(this, callback), this, filter, prepend);
},
destroy: function(notChained) {
var instance = this,
superDestroy;
if (!instance._destroyed) {
superDestroy = function(constructor) {
// don't call `hasOwnProperty` directly on obj --> it might have been overruled
Object.prototype.hasOwnProperty.call(constructor.prototype, '_destroy') && constructor.prototype._destroy.call(instance);
if (!notChained && constructor.$$super) {
instance.__classCarier__ = constructor.$$super.constructor;
superDestroy(constructor.$$super.constructor);
}
};
superDestroy(instance.constructor);
instance.detachAll();
Object.protectedProp(instance, '_destroyed', true);
}
}
};
// Patching Classes.BaseClass to make it an eventlistener that auto cleans-up:
Classes.BaseClass.mergePrototypes(Event.Listener, true)
.mergePrototypes(ClassListener, true, {}, {});
module.exports = Event;
},{"./event-base.js":7,"js-ext/extra/classes.js":11,"js-ext/lib/object.js":13}],10:[function(require,module,exports){
module.exports = require('./event-base.js');
require('./event-emitter.js');
require('./event-listener.js');
},{"./event-base.js":7,"./event-emitter.js":8,"./event-listener.js":9}],11:[function(require,module,exports){
(function (global){
/**
*
* Pollyfils for often used functionality for Functions
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module js-ext
* @submodule extra/classes.js
* @class Classes
*
*/
require('polyfill/polyfill-base.js');
require('../lib/object.js');
(function (global) {
"use strict";
var NAME = '[Classes]: ',
createHashMap = require('js-ext/extra/hashmap.js').createMap,
DEFAULT_CHAIN_CONSTRUCT, defineProperty, defineProperties,
NOOP, REPLACE_CLASS_METHODS, PROTECTED_CLASS_METHODS, PROTO_RESERVED_NAMES,
BASE_MEMBERS, createBaseClass, Classes, coreMethods;
global._ITSAmodules || Object.protectedProp(global, '_ITSAmodules', createHashMap());
/*jshint boss:true */
if (Classes=global._ITSAmodules.Classes) {
/*jshint boss:false */
module.exports = Classes; // Classes was already created
return;
}
/**
* Defines whether Classes should call their constructor in a chained way top-down.
*
* @property DEFAULT_CHAIN_CONSTRUCT
* @default true
* @type Boolean
* @protected
* @since 0.0.1
*/
DEFAULT_CHAIN_CONSTRUCT = true;
/**
* Sugarmethod for Object.defineProperty creating an unenumerable property
*
* @method defineProperty
* @param [object] {Object} The object to define the property to
* @param [name] {String} name of the property
* @param [method] {Any} value of the property
* @param [force=false] {Boolean} to force assignment when the property already exists
* @protected
* @since 0.0.1
*/
defineProperty = function (object, name, method, force) {
if (!force && (name in object)) {
return;
}
Object.defineProperty(object, name, {
configurable: true,
enumerable: false,
writable: true,
value: method
});
};
/**
* Sugarmethod for using defineProperty for multiple properties at once.
*
* @method defineProperties
* @param [object] {Object} The object to define the property to
* @param [map] {Object} object to be set
* @param [force=false] {Boolean} to force assignment when the property already exists
* @protected
* @since 0.0.1
*/
defineProperties = function (object, map, force) {
var names = Object.keys(map),
l = names.length,
i = -1,
name;
while (++i < l) {
name = names[i];
defineProperty(object, name, map[name], force);
}
};
/**
* Empty function
*
* @method NOOP
* @protected
* @since 0.0.1
*/
NOOP = function () {};
/**
* Internal hash containing the names of members which names should be transformed
*
* @property REPLACE_CLASS_METHODS
* @default {destroy: '_destroy'}
* @type Object
* @protected
* @since 0.0.1
*/
REPLACE_CLASS_METHODS = createHashMap({
destroy: '_destroy'
});
/**
* Internal hash containing protected members: those who cannot be merged into a Class
*
*
* @property PROTECTED_CLASS_METHODS
* @default {$super: true, $superProp: true, $orig: true}
* @type Object
* @protected
* @since 0.0.1
*/
PROTECTED_CLASS_METHODS = createHashMap({
$super: true,
$superProp: true,
$orig: true
});
/*jshint proto:true */
/* jshint -W001 */
/*
* Internal hash containing protected members: those who cannot be merged into a Class
*
* @property PROTO_RESERVED_NAMES
* @default {constructor: true, prototype: true, hasOwnProperty: true, isPrototypeOf: true,
* propertyIsEnumerable: true, __defineGetter__: true, __defineSetter__: true,
* __lookupGetter__: true, __lookupSetter__: true, __proto__: true}
* @type Object
* @protected
* @since 0.0.1
*/
PROTO_RESERVED_NAMES = createHashMap({
constructor: true,
prototype: true,
hasOwnProperty: true,
isPrototypeOf: true,
propertyIsEnumerable: true,
__defineGetter__: true,
__defineSetter__: true,
__lookupGetter__: true,
__lookupSetter__: true,
__proto__: true
});
/* jshint +W001 */
/*jshint proto:false */
defineProperties(Function.prototype, {
/**
* Merges the given prototypes of properties into the `prototype` of the Class.
*
* **Note1 ** to be used on instances --> ONLY on Classes
* **Note2 ** properties with getters and/or unwritable will NOT be merged
*
* The members in the hash prototypes will become members with
* instances of the merged class.
*
* By default, this method will not override existing prototype members,
* unless the second argument `force` is true.
*
* @method mergePrototypes
* @param prototypes {Object} Hash prototypes of properties to add to the prototype of this object
* @param force {Boolean} If true, existing members will be overwritten
* @chainable
*/
mergePrototypes: function (prototypes, force) {
var instance, proto, names, l, i, replaceMap, protectedMap, name, nameInProto, finalName, propDescriptor, extraInfo;
if (!prototypes) {
return;
}
instance = this; // the Class
proto = instance.prototype;
names = Object.getOwnPropertyNames(prototypes);
l = names.length;
i = -1;
replaceMap = arguments[2] || REPLACE_CLASS_METHODS; // hidden feature, used by itags
protectedMap = arguments[3] || PROTECTED_CLASS_METHODS; // hidden feature, used by itags
while (++i < l) {
name = names[i];
finalName = replaceMap[name] || name;
nameInProto = (finalName in proto);
if (!PROTO_RESERVED_NAMES[finalName] && !protectedMap[finalName] && (!nameInProto || force)) {
// if nameInProto: set the property, but also backup for chaining using $$orig
propDescriptor = Object.getOwnPropertyDescriptor(prototypes, name);
if (!propDescriptor.writable) {
console.info(NAME+'mergePrototypes will set property of '+name+' without its property-descriptor: for it is an unwritable property.');
proto[finalName] = prototypes[name];
}
else {
// adding prototypes[name] into $$orig:
instance.$$orig[finalName] || (instance.$$orig[finalName]=[]);
instance.$$orig[finalName][instance.$$orig[finalName].length] = prototypes[name];
if (typeof prototypes[name] === 'function') {
/*jshint -W083 */
propDescriptor.value = (function (originalMethodName, finalMethodName) {
return function () {
/*jshint +W083 */
// this.$own = prot;
// this.$origMethods = instance.$$orig[finalMethodName];
var context = this,
classCarierBkp = context.__classCarier__,
methodClassCarierBkp = context.__methodClassCarier__,
origPropBkp = context.__origProp__,
returnValue;
context.__methodClassCarier__ = instance;
context.__classCarier__ = null;
context.__origProp__ = finalMethodName;
returnValue = prototypes[originalMethodName].apply(context, arguments);
context.__origProp__ = origPropBkp;
context.__classCarier__ = classCarierBkp;
context.__methodClassCarier__ = methodClassCarierBkp;
return returnValue;
};
})(name, finalName);
}
Object.defineProperty(proto, finalName, propDescriptor);
}
}
else {
extraInfo = '';
nameInProto && (extraInfo = 'property is already available (you might force it to be set)');
PROTO_RESERVED_NAMES[finalName] && (extraInfo = 'property is a protected property');
protectedMap[finalName] && (extraInfo = 'property is a private property');
console.warn(NAME+'mergePrototypes is not allowed to set the property: '+name+' --> '+extraInfo);
}
}
return instance;
},
/**
* Removes the specified prototypes from the Class.
*
*
* @method removePrototypes
* @param properties {String|Array} Hash of properties to be removed from the Class
* @chainable
*/
removePrototypes: function (properties) {
var proto = this.prototype,
replaceMap = arguments[1] || REPLACE_CLASS_METHODS; // hidden feature, used by itags
Array.isArray(properties) || (properties=[properties]);
properties.forEach(function(prop) {
prop = replaceMap[prop] || prop;
delete proto[prop];
});
return this;
},
/**
* Redefines the constructor fo the Class
*
* @method setConstructor
* @param [constructorFn] {Function} The function that will serve as the new constructor for the class.
* If `undefined` defaults to `NOOP`
* @param [prototypes] {Object} Hash map of properties to be added to the prototype of the new class.
* @param [chainConstruct=true] {Boolean} Whether -during instance creation- to automaticly construct in the complete hierarchy with the given constructor arguments.
* @chainable
*/
setConstructor: function(constructorFn, chainConstruct) {
var instance = this;
if (typeof constructorFn==='boolean') {
chainConstruct = constructorFn;
constructorFn = null;
}
(typeof chainConstruct === 'boolean') || (chainConstruct=DEFAULT_CHAIN_CONSTRUCT);
instance.$$constrFn = constructorFn || NOOP;
instance.$$chainConstructed = chainConstruct ? true : false;
return instance;
},
/**
* Returns a newly created class inheriting from this class
* using the given `constructor` with the
* prototypes listed in `prototypes` merged in.
*
*
* The newly created class has the `$$super` static property
* available to access all of is ancestor's instance methods.
*
* Further methods can be added via the [mergePrototypes](#method_mergePrototypes).
*
* @example
*
* var Circle = Shape.subClass(
* function (x, y, r) {
* // arguments will automaticly be passed through to Shape's constructor
* this.r = r;
* },
* {
* area: function () {
* return this.r * this.r * Math.PI;
* }
* }
* );
*
* @method subClass
* @param [constructor] {Function} The function that will serve as constructor for the new class.
* If `undefined` defaults to `NOOP`
* @param [prototypes] {Object} Hash map of properties to be added to the prototype of the new class.
* @param [chainConstruct=true] {Boolean} Whether -during instance creation- to automaticly construct in the complete hierarchy with the given constructor arguments.
* @return the new class.
*/
subClass: function (constructor, prototypes, chainConstruct) {
var instance = this,
constructorClosure = {},
baseProt, proto, constrFn;
if (typeof constructor === 'boolean') {
constructor = null;
prototypes = null;
chainConstruct = constructor;
}
else {
if (Object.isObject(constructor)) {
chainConstruct = prototypes;
prototypes = constructor;
constructor = null;
}
if (typeof prototypes === 'boolean') {
chainConstruct = prototypes;
prototypes = null;
}
}
(typeof chainConstruct === 'boolean') || (chainConstruct=DEFAULT_CHAIN_CONSTRUCT);
constrFn = constructor || NOOP;
constructor = function() {
constructorClosure.constructor.$$constrFn.apply(this, arguments);
};
constructor = (function(originalConstructor) {
return function() {
var context = this;
if (constructorClosure.constructor.$$chainConstructed) {
context.__classCarier__ = constructorClosure.constructor.$$super.constructor;
context.__origProp__ = 'constructor';
context.__classCarier__.apply(context, arguments);
context.$origMethods = constructorClosure.constructor.$$orig.constructor;
}
context.__classCarier__ = constructorClosure.constructor;
context.__origProp__ = 'constructor';
originalConstructor.apply(context, arguments);
// only call aferInit on the last constructor of the chain:
(constructorClosure.constructor===context.constructor) && context.afterInit();
};
})(constructor);
baseProt = instance.prototype;
proto = Object.create(baseProt);
constructor.prototype = proto;
// webkit doesn't let all objects to have their constructor redefined
// when directly assigned. Using `defineProperty will work:
Object.defineProperty(proto, 'constructor', {value: constructor});
constructor.$$chainConstructed = chainConstruct ? true : false;
constructor.$$super = baseProt;
constructor.$$orig = {
constructor: constructor
};
constructor.$$constrFn = constrFn;
constructorClosure.constructor = constructor;
prototypes && constructor.mergePrototypes(prototypes, true);
return constructor;
}
});
global._ITSAmodules.Classes = Classes = {};
/**
* Base properties for every Class
*
*
* @property BASE_MEMBERS
* @type Object
* @protected
* @since 0.0.1
*/
BASE_MEMBERS = {
/**
* Transformed from `destroy` --> when `destroy` gets invoked, the instance will invoke `_destroy` through the whole chain.
* Defaults to `NOOP`, so that it can be always be invoked.
*
* @method _destroy
* @private
* @chainable
* @since 0.0.1
*/
_destroy: NOOP,
/**
* Transformed from `destroy` --> when `destroy` gets invoked, the instance will invoke `_destroy` through the whole chain.
* Defaults to `NOOP`, so that it can be always be invoked.
*
* @method afterInit
* @private
* @chainable
* @since 0.0.1
*/
afterInit: NOOP,
/**
* Calls `_destroy` on through the class-chain on every level (bottom-up).
* _destroy gets defined when the itag defines `destroy` --> transformation under the hood.
*
* @method destroy
* @param [notChained=false] {Boolean} set this `true` to prevent calling `destroy` up through the chain
* @chainable
* @since 0.0.1
*/
destroy: function(notChained) {
var instance = this,
superDestroy;
if (!instance._destroyed) {
superDestroy = function(constructor) {
// don't call `hasOwnProperty` directly on obj --> it might have been overruled
Object.prototype.hasOwnProperty.call(constructor.prototype, '_destroy') && constructor.prototype._destroy.call(instance);
if (!notChained && constructor.$$super) {
instance.__classCarier__ = constructor.$$super.constructor;
superDestroy(constructor.$$super.constructor);
}
};
// instance.detachAll(); <-- is what Event will add
superDestroy(instance.constructor);
Object.protectedProp(instance, '_destroyed', true);
}
return instance;
}
};
coreMethods = Classes.coreMethods = {
/**
* Returns the instance, yet sets an internal flag to a higher Class (1 level up)
*
* @property $super
* @chainable
* @for BaseClass
* @since 0.0.1
*/
$super: {
get: function() {
var instance = this;
instance.__classCarier__ || (instance.__classCarier__= instance.__methodClassCarier__);
instance.__$superCarierStart__ || (instance.__$superCarierStart__=instance.__classCarier__);
instance.__classCarier__ = instance.__classCarier__ && instance.__classCarier__.$$super.constructor;
return instance;
}
},
/**
* Calculated value of the specified member at the parent-Class.
*
* @method $superProp
* @return {Any}
* @since 0.0.1
*/
$superProp: {
configurable: true,
writable: true,
value: function(/* member, *args */) {
var instance = this,
classCarierReturn = instance.__$superCarierStart__ || instance.__classCarier__ || instance.__methodClassCarier__,
currentClassCarier = instance.__classCarier__ || instance.__methodClassCarier__,
args = arguments,
superClass, superPrototype, firstArg, returnValue;
instance.__$superCarierStart__ = null;
if (args.length === 0) {
instance.__classCarier__ = classCarierReturn;
return;
}
superClass = currentClassCarier.$$super.constructor,
superPrototype = superClass.prototype,
firstArg = Array.prototype.shift.apply(args); // will decrease the length of args with one
if ((firstArg==='constructor') && currentClassCarier.$$chainConstructed) {
console.warn('the constructor of this Class cannot be invoked manually, because it is chainConstructed');
return currentClassCarier;
}
if (typeof superPrototype[firstArg] === 'function') {
instance.__classCarier__ = superClass;
returnValue = superPrototype[firstArg].apply(instance, args);
}
instance.__classCarier__ = classCarierReturn;
return returnValue || superPrototype[firstArg];
}
},
/**
* Invokes the original method (from inside where $orig is invoked).
* Any arguments will be passed through to the original method.
*
* @method $orig
* @return {Any}
* @since 0.0.1
*/
$orig: {
configurable: true,
writable: true,
value: function() {
var instance = this,
classCarierReturn = instance.__$superCarierStart__,
currentClassCarier = instance.__classCarier__ || instance.__methodClassCarier__,
args = arguments,
propertyName = instance.__origProp__,
returnValue, origArray, orig, item;
instance.__$superCarierStart__ = null;
origArray = currentClassCarier.$$orig[propertyName];
instance.__origPos__ || (instance.__origPos__ = []);
// every class can have its own overruled $orig for even the same method
// first: seek for the item that matches propertyName/classRef:
instance.__origPos__.some(function(element) {
if ((element.propertyName===propertyName) && (element.classRef===currentClassCarier)) {
item = element;
}
return item;
});
if (!item) {
item = {
propertyName: propertyName,
classRef: currentClassCarier,
position: origArray.length-1
};
instance.__origPos__.push(item);
}
if (item.position===0) {
return undefined;
}
item.position--;
orig = origArray[item.position];
if (typeof orig === 'function') {
instance.__classCarier__ = currentClassCarier;
returnValue = orig.apply(instance, args);
}
instance.__classCarier__ = classCarierReturn;
item.position++;
return returnValue || orig;
}
}
};
/**
* Creates the base Class: the highest Class in the hierarchy of all Classes.
* Will get extra properties merge into its prototype, which leads into the formation of `BaseClass`.
*
* @method createBaseClass
* @protected
* @return {Class}
* @for Classes
* @since 0.0.1
*/
createBaseClass = function () {
var InitClass = function() {};
return Function.prototype.subClass.apply(InitClass, arguments);
};
/**
* The base BaseClass: the highest Class in the hierarchy of all Classes.
*
* @property BaseClass
* @type Class
* @since 0.0.1
*/
Object.protectedProp(Classes, 'BaseClass', createBaseClass().mergePrototypes(BASE_MEMBERS, true, {}, {}));
// because `mergePrototypes` cannot merge object-getters, we will add the getter `$super` manually:
Object.defineProperties(Classes.BaseClass.prototype, coreMethods);
/**
* Returns a base class with the given constructor and prototype methods
*
* @method createClass
* @param [constructor] {Function} constructor for the class
* @param [prototype] {Object} Hash map of prototype members of the new class
* @return {Class} the new class
*/
Object.protectedProp(Classes, 'createClass', Classes.BaseClass.subClass.bind(Classes.BaseClass));
module.exports = Classes;
}(typeof global !== 'undefined' ? global : /* istanbul ignore next */ this));
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"../lib/object.js":13,"js-ext/extra/hashmap.js":12,"polyfill/polyfill-base.js":16}],12:[function(require,module,exports){
module.exports=require(4)
},{}],13:[function(require,module,exports){
/**
*
* Pollyfils for often used functionality for Objects
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module js-ext
* @submodule lib/object.js
* @class Object
*
*/
"use strict";
require('polyfill/polyfill-base.js');
var createHashMap = require('js-ext/extra/hashmap.js').createMap,
NATIVE_OBJECT_OBSERVE = !!Object.observe,
later = require('utils').later,
TYPES = createHashMap({
'undefined' : true,
'number' : true,
'boolean' : true,
'string' : true,
'[object Function]' : true,
'[object RegExp]' : true,
'[object Array]' : true,
'[object Date]' : true,
'[object Error]' : true,
'[object Promise]' : true
}),
_watchers = [],
POLL_OBSERVE = 100,
// Define configurable, writable and non-enumerable props
// if they don't exist.
defineProperty = function (object, name, method, force) {
if (!force && (name in object)) {
return;
}
Object.defineProperty(object, name, {
configurable: true,
enumerable: false,
writable: true,
value: method
});
},
defineProperties = function (object, map, force) {
var names = Object.keys(map),
l = names.length,
i = -1,
name;
while (++i < l) {
name = names[i];
defineProperty(object, name, map[name], force);
}
},
_each = function (obj, fn, context) {
var keys = Object.keys(obj),
l = keys.length,
i = -1,
key;
while (++i < l) {
key = keys[i];
fn.call(context, obj[key], key, obj);
}
return obj;
},
cloneObj = function(obj, descriptors) {
var copy, i, len, value;
// Handle Array
if (obj instanceof Array) {
copy = [];
len = obj.length;
for (i=0; i<len; i++) {
value = obj[i];
copy[i] = ((value===null) || (typeof value!=='object')) ? value : cloneObj(value, descriptors);
}
return copy;
}
// Handle Date
if (obj instanceof Date) {
copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
// Handle Object
else if (obj instanceof Object) {
copy = obj.deepClone(descriptors);
}
return copy;
},
valuesAreTheSame = function(value1, value2) {
var same, i, len;
// complex values need to be inspected differently:
if (Object.isObject(value1)) {
same = Object.isObject(value2) ? value1.sameValue(value2) : false;
}
else if (Array.isArray(value1)) {
if (Array.isArray(value2)) {
len = value1.length;
if (len===value2.length) {
same = true;
for (i=0; same && (i<len); i++) {
same = valuesAreTheSame(value1[i], value2[i]);
}
}
else {
same = false;
}
}
else {
same = false;
}
}
else if (value1 instanceof Date) {
same = (value2 instanceof Date) ? (value1.getTime()===value2.getTime()) : false;
}
else {
same = (value1===value2);
}
return same;
},
watchObject = function(obj, callback) {
var watcher;
watcher = {
obj: obj,
cb: callback,
cloneObj: obj.deepClone(true),
timer: later(function() {
if (!obj.sameValue(watcher.cloneObj)) {
watcher.cloneObj = obj.deepClone(true);
callback();
}
}, POLL_OBSERVE, true)
};
_watchers[_watchers.length] = watcher;
},
unWatchObject = function(obj, callback) {
var currentWatcher;
_watchers.some(function(watcher) {
if ((watcher.obj===obj) && (watcher.callback===callback)) {
currentWatcher = watcher;
}
return currentWatcher;
});
if (currentWatcher) {
currentWatcher.timer.cancel();
_watchers.remove(currentWatcher);
}
};
/**
* Pollyfils for often used functionality for objects
* @class Object
*/
defineProperties(Object.prototype, {
/**
* Loops through all properties in the object. Equivalent to Array.forEach.
* The callback is provided with the value of the property, the name of the property
* and a reference to the whole object itself.
* The context to run the callback in can be overriden, otherwise it is undefined.
*
* @method each
* @param fn {Function} Function to be executed on each item in the object. It will receive
* value {any} value of the property
* key {string} name of the property
* obj {Object} the whole of the object
* @chainable
*/
each: function (fn, context) {
if (context) return _each(this, fn, context);
var keys = Object.keys(this),
l = keys.length,
i = -1,
key;
while (++i < l) {
key = keys[i];
fn(this[key], key, this);
}
return this;
},
/**
* Loops through the properties in an object until the callback function returns *truish*.
* The callback is provided with the value of the property, the name of the property
* and a reference to the whole object itself.
* The order in which the elements are visited is not predictable.
* The context to run the callback in can be overriden, otherwise it is undefined.
*
* @method some
* @param fn {Function} Function to be executed on each item in the object. It will receive
* value {any} value of the property
* key {string} name of the property
* obj {Object} the whole of the object
* @return {Boolean} true if the loop was interrupted by the callback function returning *truish*.
*/
some: function (fn, context) {
var keys = Object.keys(this),
l = keys.length,
i = -1,
key;
while (++i < l) {
key = keys[i];
if (fn.call(context, this[key], key, this)) {
return true;
}
}
return false;
},
/**
* Loops through the properties in an object until the callback assembling a new object
* with its properties set to the values returned by the callback function.
* If the callback function returns `undefined` the property will not be copied to the new object.
* The resulting object will have the same keys as the original, except for those where the callback
* returned `undefined` which will have dissapeared.
* The callback is provided with the value of the property, the name of the property
* and a reference to the whole object itself.
* The context to run the callback in can be overriden, otherwise it is undefined.
*
* @method map
* @param fn {Function} Function to be executed on each item in the object. It will receive
* value {any} value of the property
* key {string} name of the property
* obj {Object} the whole of the object
* @return {Object} The new object with its properties set to the values returned by the callback function.
*/
map: function (fn, context) {
var keys = Object.keys(this),
l = keys.length,
i = -1,
m = {},
val, key;
while (++i < l) {
key = keys[i];
val = fn.call(context, this[key], key, this);
if (val !== undefined) {
m[key] = val;
}
}
return m;
},
/**
* Returns the keys of the object: the enumerable properties.
*
* @method keys
* @return {Array} Keys of the object
*/
keys: function () {
return Object.keys(this);
},
/**
* Checks whether the given property is a key: an enumerable property.
*
* @method hasKey
* @param property {String} the property to check for
* @return {Boolean} Keys of the object
*/
hasKey: function (property) {
return this.hasOwnProperty(property) && this.propertyIsEnumerable(property);
},
/**
* Observes changes of the instance. On any changes, the callback will be invoked.
* Uses a polyfill on environments that don't support native Object.observe.
*
* The callback comes without arguments (native Object.oserve does, but non-native doesn't)
* so, they cannot be used.
*
* @method observe
* @chainable
*/
observe: function (callback) {
if (typeof callback==='function') {
if (NATIVE_OBJECT_OBSERVE) {
Object.observe(this, callback);
}
else {
watchObject(this, callback);
}
}
return this;
},
/**
* Un-observes changes that are registered with `observe`.
* Uses a polyfill on environments that don't support native Object.observe.
*
* @method observe
* @chainable
*/
unobserve: function (callback) {
if (typeof callback==='function') {
if (NATIVE_OBJECT_OBSERVE) {
Object.unobserve(this, callback);
}
else {
unWatchObject(this, callback);
}
}
return this;
},
/**
* Returns the number of keys of the object
*
* @method size
* @return {Number} Number of items
*/
size: function () {
return Object.keys(this).length;
},
/**
* Loops through the object collection the values of all its properties.
* It is the counterpart of the [`keys`](#method_keys).
*
* @method values
* @return {Array} values of the object
*/
values: function () {
var keys = Object.keys(this),
i = -1,
len = keys.length,
values = [];
while (++i < len) {
values.push(this[keys[i]]);
}
return values;
},
/**
* Returns true if the object has no own members
*
* @method isEmpty
* @return {Boolean} true if the object is empty
*/
isEmpty: function () {
for (var key in this) {
if (this.hasOwnProperty(key)) return false;
}
return true;
},
/**
* Returns a shallow copy of the object.
* It does not clone objects within the object, it does a simple, shallow clone.
* Fast, mostly useful for plain hash maps.
*
* @method shallowClone
* @param [options.descriptors=false] {Boolean} If true, the full descriptors will be set. This takes more time, but avoids any info to be lost.
* @return {Object} shallow copy of the original
*/
shallowClone: function (descriptors) {
var instance = this,
m = Object.create(Object.getPrototypeOf(instance)),
keys = Object.getOwnPropertyNames(instance),
l = keys.length,
i = -1,
key, propDescriptor;
while (++i < l) {
key = keys[i];
if (descriptors) {
propDescriptor = Object.getOwnPropertyDescriptor(instance, key);
if (!propDescriptor.writable) {
m[key] = instance[key];
}
else {
Object.defineProperty(m, key, propDescriptor);
}
}
else {
m[key] = instance[key];
}
}
return m;
},
/**
* Compares this object with the reference-object whether they have the same value.
* Not by reference, but their content as simple types.
*
* Compares both JSON.stringify objects
*
* @method sameValue
* @param refObj {Object} the object to compare with
* @return {Boolean} whether both objects have the same value
*/
sameValue: function(refObj) {
var instance = this,
keys = Object.getOwnPropertyNames(instance),
l = keys.length,
i = -1,
same, key;
same = (l===refObj.size());
// loop through the members:
while (same && (++i < l)) {
key = keys[i];
same = refObj.hasKey(key) ? valuesAreTheSame(instance[key], refObj[key]) : false;
}
return same;
},
/**
* Returns a deep copy of the object.
* Only handles members of primary types, Dates, Arrays and Objects.
* Will clone all the properties, also the non-enumerable.
*
* @method deepClone
* @param [descriptors=false] {Boolean} If true, the full descriptors will be set. This takes more time, but avoids any info to be lost.
* @param [proto] {Object} Another prototype for the new object.
* @return {Object} deep-copy of the original
*/
deepClone: function (descriptors, proto) {
var instance = this,
m = Object.create(proto || Object.getPrototypeOf(instance)),
keys = Object.getOwnPropertyNames(instance),
l = keys.length,
i = -1,
key, value, propDescriptor;
// loop through the members:
while (++i < l) {
key = keys[i];
value = instance[key];
if (descriptors) {
propDescriptor = Object.getOwnPropertyDescriptor(instance, key);
if (propDescriptor.writable) {
Object.defineProperty(m, key, propDescriptor);
}
else {
m[key] = value;
}
if ((value!==null) && (typeof value==='object') && ((typeof propDescriptor.get)!=='function') && ((typeof propDescriptor.set)!=='function') ) {
m[key] = cloneObj(value, true);
}
}
else {
m[key] = ((value===null) || (typeof value!=='object')) ? value : cloneObj(value);
}
}
return m;
},
/**
* Transforms the object into an array with 'key/value' objects
*
* @example
* {country: 'USA', Continent: 'North America'} --> [{key: 'country', value: 'USA'}, {key: 'Continent', value: 'North America'}]
*
* @method toArray
* @param [options] {Object}
* @param [options.key] {String} to overrule the default `key`-property-name
* @param [options.value] {String} to overrule the default `value`-property-name
* @return {Array} the transformed Array-representation of the object
*/
toArray: function(options) {
var newArray = [],
keyIdentifier = (options && options.key) || 'key',
valueIdentifier = (options && options.value) || 'value';
this.each(function(value, key) {
var obj = {};
obj[keyIdentifier] = key;
obj[valueIdentifier] = value;
newArray[newArray.length] = obj;
});
return newArray;
},
/**
* Merges into this object the properties of the given object.
* If the second argument is true, the properties on the source object will be overwritten
* by those of the second object of the same name, otherwise, they are preserved.
*
* @method merge
* @param obj {Object} Object with the properties to be added to the original object
* @param [options] {Object}
* @param [options.force=false] {Boolean} If true, the properties in `obj` will override those of the same name
* in the original object
* @param [options.full=false] {Boolean} If true, also any non-enumerable properties will be merged
* @param [options.replace=false] {Boolean} If true, only properties that already exist on the instance will be merged (forced replaced). No need to set force as well.
* @param [options.descriptors=false] {Boolean} If true, the full descriptors will be set. This takes more time, but avoids any info to be lost.
* @chainable
*/
merge: function (obj, options) {
var instance = this,
i = -1,
keys, l, key, force, replace, descriptors, propDescriptor;
if (!Object.isObject(obj)) {
return instance;
}
options || (options={});
keys = options.full ? Object.getOwnPropertyNames(obj) : Object.keys(obj);
l = keys.length;
force = options.force;
replace = options.replace;
descriptors = options.descriptors;
// we cannot use obj.each --> obj might be an object defined through Object.create(null) and missing Object.prototype!
while (++i < l) {
key = keys[i];
if ((force && !replace) || (!replace && !(key in instance)) || (replace && (key in instance))) {
if (descriptors) {
propDescriptor = Object.getOwnPropertyDescriptor(obj, key);
if (!propDescriptor.writable) {
instance[key] = obj[key];
}
else {
Object.defineProperty(instance, key, propDescriptor);
}
}
else {
instance[key] = obj[key];
}
}
}
return instance;
}
});
/**
* Returns true if the item is an object, but no Array, Function, RegExp, Date or Error object
*
* @method isObject
* @static
* @return {Boolean} true if the object is empty
*/
Object.isObject = function (item) {
return !!(!TYPES[typeof item] && !TYPES[({}.toString).call(item)] && item);
};
/**
* Creates a protected property on the object.
*
* @method protectedProp
* @static
*/
Object.protectedProp = function(obj, property, value) {
Object.defineProperty(obj, property, {
configurable: false,
enumerable: false,
writable: false,
value: value
});
};
/**
* Returns a new object resulting of merging the properties of the given objects.
* The copying is shallow, complex properties will reference the very same object.
* Properties in later objects do **not overwrite** properties of the same name in earlier objects.
* If any of the objects is missing, it will be skiped.
*
* @example
*
* var foo = function (config) {
* config = Object.merge(config, defaultConfig);
* }
*
* @method merge
* @static
* @param obj* {Object} Objects whose properties are to be merged
* @return {Object} new object with the properties merged in.
*/
Object.merge = function () {
var m = {};
Array.prototype.forEach.call(arguments, function (obj) {
if (obj) m.merge(obj);
});
return m;
};
},{"js-ext/extra/hashmap.js":12,"polyfill/polyfill-base.js":16,"utils":17}],14:[function(require,module,exports){
(function (global){
// based upon https://gist.github.com/jonathantneal/3062955
(function (global) {
"use strict";
global.Element && (function(ElementPrototype) {
ElementPrototype.matchesSelector ||
(ElementPrototype.matchesSelector = ElementPrototype.mozMatchesSelector ||
ElementPrototype.msMatchesSelector ||
ElementPrototype.oMatchesSelector ||
ElementPrototype.webkitMatchesSelector ||
function (selector) {
var node = this,
nodes = (node.parentNode || global.document).querySelectorAll(selector),
i = -1;
while (nodes[++i] && (nodes[i] !== node));
return !!nodes[i];
}
);
}(global.Element.prototype));
}(typeof global !== 'undefined' ? global : /* istanbul ignore next */ this));
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],15:[function(require,module,exports){
(function (global){
(function (global) {
"use strict";
var CONSOLE = {
log: function() { /* NOOP */ },
info: function() { /* NOOP */ },
warn: function() { /* NOOP */ },
error: function() { /* NOOP */ }
};
global.console || (function(GlobalPrototype) {
GlobalPrototype.console = CONSOLE;
}(global.prototype));
module.exports = CONSOLE;
}(typeof global !== 'undefined' ? global : /* istanbul ignore next */ this));
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],16:[function(require,module,exports){
require('./lib/window.console.js');
require('./lib/matchesselector.js');
},{"./lib/matchesselector.js":14,"./lib/window.console.js":15}],17:[function(require,module,exports){
module.exports = {
idGenerator: require('./lib/idgenerator.js').idGenerator,
later: require('./lib/timers.js').later,
async: require('./lib/timers.js').async
};
},{"./lib/idgenerator.js":18,"./lib/timers.js":19}],18:[function(require,module,exports){
"use strict";
require('polyfill/polyfill-base.js');
var UNDEFINED_NS = '__undefined__',
createHashMap = require('js-ext/extra/hashmap.js').createMap,
namespaces = createHashMap();
/**
* Collection of various utility functions.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module utils
* @class Utils
* @static
*/
/**
* Generates an unique id with the signature: "namespace-follownr"
*
* @example
*
* var generator = require('core-utils-idgenerator');
*
* console.log(generator()); // --> 1
* console.log(generator()); // --> 2
* console.log(generator(1000)); // --> 1000
* console.log(generator()); // --> 1001
* console.log(generator('Parcel, 500')); // -->"Parcel-500"
* console.log(generator('Parcel')); // -->"Parcel-501"
*
*
* @method idGenerator
* @param [namespace] {String} namespace to prepend the generated id.
* When ignored, the generator just returns a number.
* @param [start] {Number} startvalue for the next generated id. Any further generated id's will preceed this id.
* If `start` is lower or equal than the last generated id, it will be ignored.
* @return {Number|String} an unique id. Either a number, or a String (digit prepended with "namespace-")
*/
module.exports.idGenerator = function(namespace, start) {
// in case `start` is set at first argument, transform into (null, start)
(typeof namespace==='number') && (start=namespace) && (namespace=null);
namespace || (namespace=UNDEFINED_NS);
if (!namespaces[namespace]) {
namespaces[namespace] = start || 1;
}
else if (start && (namespaces[namespace]<start)) {
namespaces[namespace] = start;
}
return (namespace===UNDEFINED_NS) ? namespaces[namespace]++ : namespace+'-'+namespaces[namespace]++;
};
},{"js-ext/extra/hashmap.js":12,"polyfill/polyfill-base.js":22}],19:[function(require,module,exports){
(function (process){
/**
* Collection of various utility functions.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module utils
* @class Utils
* @static
*/
"use strict";
require('polyfill/polyfill-base.js');
var NAME = '[utils-timers]: ',
_asynchronizer, _async;
/**
* Forces a function to be run asynchronously, but as fast as possible. In Node.js
* this is achieved using `setImmediate` or `process.nextTick`.
*
* @method _asynchronizer
* @param callbackFn {Function} The function to call asynchronously
* @static
* @private
**/
_asynchronizer = (typeof setImmediate !== 'undefined') ? function (fn) {setImmediate(fn);} :
((typeof process !== 'undefined') && process.nextTick) ? process.nextTick : function (fn) {setTimeout(fn, 0);};
/**
* Invokes the callbackFn once in the next turn of the JavaScript event loop. If the function
* requires a specific execution context or arguments, wrap it with Function.bind.
*
* I.async returns an object with a cancel method. If the cancel method is
* called before the callback function, the callback function won't be called.
*
* @method async
* @param {Function} callbackFn
* @param [invokeAfterFn=true] {boolean} set to false to prevent the _afterSyncFn to be invoked
* @return {Object} An object with a cancel method. If the cancel method is
* called before the callback function, the callback function won't be called.
**/
_async = function (callbackFn, invokeAfterFn) {
console.log(NAME, 'async');
var canceled;
invokeAfterFn = (typeof invokeAfterFn === 'boolean') ? invokeAfterFn : true;
(typeof callbackFn==='function') && _asynchronizer(function () {
if (!canceled) {
console.log(NAME, 'async is running its callbakcFn');
callbackFn();
}
});
return {
cancel: function () {
canceled = true;
}
};
};
/**
* Invokes the callbackFn once in the next turn of the JavaScript event loop. If the function
* requires a specific execution context or arguments, wrap it with Function.bind.
*
* I.async returns an object with a cancel method. If the cancel method is
* called before the callback function, the callback function won't be called.
*
* @method async
* @param {Function} callbackFn
* @param [invokeAfterFn=true] {boolean} set to false to prevent the _afterSyncFn to be invoked
* @return {Object} An object with a cancel method. If the cancel method is
* called before the callback function, the callback function won't be called.
**/
module.exports.async = _async;
/**
* Invokes the callbackFn after a timeout (asynchronous). If the function
* requires a specific execution context or arguments, wrap it with Function.bind.
*
* To invoke the callback function periodic, set 'periodic' either 'true', or specify a second timeout.
* If number, then periodic is considered 'true' but with a perdiod defined by 'periodic',
* which means: the first timer executes after 'timeout' and next timers after 'period'.
*
* I.later returns an object with a cancel method. If the cancel() method is
* called before the callback function, the callback function won't be called.
*
* @method later
* @param callbackFn {Function} the function to execute.
* @param [timeout] {Number} the number of milliseconds to wait until the callbackFn is executed.
* when not set, the callback function is invoked once in the next turn of the JavaScript event loop.
* @param [periodic] {boolean|Number} if true, executes continuously at supplied, if number, then periodic is considered 'true' but with a perdiod
* defined by 'periodic', which means: the first timer executes after 'timeout' and next timers after 'period'.
* The interval executes until canceled.
* @return {object} a timer object. Call the cancel() method on this object to stop the timer.
*/
module.exports.later = function (callbackFn, timeout, periodic) {
console.log(NAME, 'later --> timeout: '+timeout+'ms | periodic: '+periodic);
var canceled = false;
if (!timeout) {
return _async(callbackFn);
}
var wrapper = function() {
// nodejs may execute a callback, so in order to preserve
// the cancel() === no more runny-run, we have to build in an extra conditional
if (!canceled) {
console.log(NAME, 'later is running its callbackFn');
callbackFn();
// we are NOT using setInterval, because that leads to problems when the callback
// lasts longer than the interval. Instead, we use the interval as inbetween-phase
// between the separate callbacks.
id = periodic ? setTimeout(wrapper, (typeof periodic==='number') ? periodic : timeout) : null;
}
},
id;
(typeof callbackFn==='function') && (id=setTimeout(wrapper, timeout));
return {
cancel: function() {
canceled = true;
id && clearTimeout(id);
// break closure:
id = null;
}
};
};
}).call(this,require('_process'))
},{"_process":5966,"polyfill/polyfill-base.js":22}],20:[function(require,module,exports){
module.exports=require(14)
},{}],21:[function(require,module,exports){
module.exports=require(15)
},{}],22:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":20,"./lib/window.console.js":21}],23:[function(require,module,exports){
module.exports=require(14)
},{}],24:[function(require,module,exports){
module.exports=require(15)
},{}],25:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":23,"./lib/window.console.js":24}],26:[function(require,module,exports){
module.exports=require(4)
},{}],27:[function(require,module,exports){
/**
*
* Pollyfils for often used functionality for Arrays
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module js-ext
* @submodule lib/array.js
* @class Array
*
*/
"use strict";
require('polyfill/polyfill-base.js');
var cloneObj = function(obj) {
var copy, i, len, value;
// Handle Array
if (obj instanceof Array) {
copy = [];
len = obj.length;
for (i=0; i<len; i++) {
value = obj[i];
copy[i] = ((value===null) || (typeof value!=='object')) ? value : cloneObj(value);
}
return copy;
}
// Handle Date
if (obj instanceof Date) {
copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
// Handle Object
else if (obj instanceof Object) {
copy = obj.deepClone();
}
return copy;
};
(function(ArrayPrototype) {
/**
* Checks whether an item is inside the Array.
* Alias for (array.indexOf(item) > -1)
*
* @method contains
* @param item {Any} the item to seek
* @return {Boolean} whether the item is part of the Array
*/
Array.contains || (ArrayPrototype.contains=function(item) {
return (this.indexOf(item) > -1);
});
/**
* Removes an item from the array
*
* @method remove
* @param item {any|Array} the item (or an hash of items) to be removed
* @param [arrayItem=false] {Boolean} whether `item` is an arrayItem that should be treated as a single item to be removed
* You need to set `arrayItem=true` in those cases. Otherwise, all single items from `item` are removed separately.
* @chainable
*/
Array.remove || (ArrayPrototype.remove=function(item, arrayItem) {
var instance = this,
removeItem = function(oneItem) {
var index = instance.indexOf(oneItem);
(index > -1) && instance.splice(index, 1);
};
if (!arrayItem && Array.isArray(item)) {
item.forEach(removeItem);
}
else {
removeItem(item);
}
return instance;
});
/**
* Replaces an item in the array. If the previous item is not part of the array, the new item is appended.
*
* @method replace
* @param prevItem {any} the item to be replaced
* @param newItem {any} the item to be added
* @chainable
*/
Array.replace || (ArrayPrototype.replace=function(prevItem, newItem) {
var instance = this,
index = instance.indexOf(prevItem);
(index!==-1) ? instance.splice(index, 1, newItem) : instance.push(newItem);
return instance;
});
/**
* Inserts an item in the array at the specified position. If index is larger than array.length, the new item(s) will be appended.
*
* @method insertAt
* @param item {any|Array} the item to be replaced, may be an Array of items
* @param index {Number} the position where to add the item(s). When larger than Array.length, the item(s) will be appended.
* @chainable
*/
Array.insertAt || (ArrayPrototype.insertAt=function(item, index) {
this.splice(index, 0, item);
return this;
});
/**
* Shuffles the items in the Array randomly
*
* @method shuffle
* @chainable
*/
Array.shuffle || (ArrayPrototype.shuffle=function() {
var instance = this,
counter = instance.length,
temp, index;
// While there are elements in the instance
while (counter>0) {
// Pick a random index
index = Math.floor(Math.random() * counter);
// Decrease counter by 1
counter--;
// And swap the last element with it
temp = instance[counter];
instance[counter] = instance[index];
instance[index] = temp;
}
return instance;
});
/**
* Returns a deep copy of the Array.
* Only handles members of primary types, Dates, Arrays and Objects.
*
* @method deepClone
* @return {Array} deep-copy of the original
*/
ArrayPrototype.deepClone = function () {
return cloneObj(this);
};
}(Array.prototype));
},{"polyfill/polyfill-base.js":32}],28:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":26,"polyfill/polyfill-base.js":32,"utils":33}],29:[function(require,module,exports){
/**
*
* Pollyfils for often used functionality for Strings
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module js-ext
* @submodule lib/string.js
* @class String
*
*/
"use strict";
(function(StringPrototype) {
var SUBREGEX = /\{\s*([^|}]+?)\s*(?:\|([^}]*))?\s*\}/g,
DATEPATTERN = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/,
WHITESPACE_CLASS = "[\\s\uFEFF\xA0]+",
TRIM_LEFT_REGEX = new RegExp('^' + WHITESPACE_CLASS),
TRIM_RIGHT_REGEX = new RegExp(WHITESPACE_CLASS + '$'),
TRIMREGEX = new RegExp(TRIM_LEFT_REGEX.source + '|' + TRIM_RIGHT_REGEX.source, 'g'),
PATTERN_EMAIL = new RegExp('^[\\w!#$%&\'*+/=?`{|}~^-]+(?:\\.[\\w!#$%&\'*+/=?`{|}~^-]+)*@(?:[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]\\.)+[a-zA-Z]{2,}$'),
PATTERN_URLEND = '([a-zA-Z0-9]+\\.)*(?:[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]\\.)+[a-zA-Z]{2,}(/[\\w-]+)*$',
PATTERN_URLHTTP = new RegExp('^http://'+PATTERN_URLEND),
PATTERN_URLHTTPS = new RegExp('^https://'+PATTERN_URLEND),
PATTERN_URL = new RegExp('^(https?://)?'+PATTERN_URLEND),
PATTERN_INTEGER = /^(([-]?[1-9][0-9]*)|0)$/,
PATTERN_FLOAT_START = '^([-]?(([1-9][0-9]*)|0))?(\\',
PATTERN_FLOAT_END = '[0-9]+)?$',
PATTERN_FLOAT_COMMA = new RegExp(PATTERN_FLOAT_START + ',' + PATTERN_FLOAT_END),
PATTERN_FLOAT_DOT = new RegExp(PATTERN_FLOAT_START + '.' + PATTERN_FLOAT_END),
PATTERN_HEX_COLOR_ALPHA = /^#?[0-9A-F]{4}([0-9A-F]{4})?$/,
PATTERN_HEX_COLOR = /^#?[0-9A-F]{3}([0-9A-F]{3})?$/,
replaceBKP;
/**
* Checks whether the substring is part if this String.
* Alias for (String.indexOf(substring) > -1)
*
* @method contains
* @param substring {String} the substring to test for
* @param [caseInsensitive=false] {Boolean} whether to ignore case-sensivity
* @return {Boolean} whether the substring is found
*/
String.contains || (StringPrototype.contains=function(substring, caseInsensitive) {
return caseInsensitive ? (this.toLowerCase().indexOf(substring.toLowerCase()) > -1) : (this.indexOf(substring) > -1);
});
/**
* Checks if the string ends with the value specified by `test`
*
* @method endsWith
* @param test {String} the string to test for
* @param [caseInsensitive=false] {Boolean} whether to ignore case-sensivity
* @return {Boolean} whether the string ends with `test`
*/
// NOTE: we ALWAYS set this method --> ES6 native `startsWiths` lacks the second argument
StringPrototype.endsWith=function(test, caseInsensitive) {
return (new RegExp(test+'$', caseInsensitive ? 'i': '')).test(this);
};
/**
* Checks if the string can be parsed into a number when using `parseInt()`
*
* @method parsable
* @return {Boolean} whether the string is parsable
*/
String.parsable || (StringPrototype.parsable=function() {
// strange enough, NaN doen't let compare itself, so we need a strange test:
// parseInt(value, 10)===parseInt(value, 10)
// which returns `true` for a parsable value, otherwise false
return (parseInt(this)===parseInt(this));
});
/**
* Checks if the string starts with the value specified by `test`
*
* @method startsWith
* @param test {String} the string to test for
* @param [caseInsensitive=false] {Boolean} whether to ignore case-sensivity
* @return {Boolean} whether the string starts with `test`
*/
// NOTE: we ALWAYS set this method --> ES6 native `startsWiths` lacks the second argument
StringPrototype.startsWith=function(test, caseInsensitive) {
return (new RegExp('^'+test, caseInsensitive ? 'i': '')).test(this);
};
/**
* Performs `{placeholder}` substitution on a string. The object passed
* provides values to replace the `{placeholder}`s.
* `{placeholder}` token names must match property names of the object.
*
* `{placeholder}` tokens that are undefined on the object map will be removed.
*
* @example
* var greeting = '{message} {who}!';
* greeting.substitute({message: 'Hello'}); // results into 'Hello !'
*
* @method substitute
* @param obj {Object} Object containing replacement values.
* @return {String} the substitute result.
*/
String.substitute || (StringPrototype.substitute=function(obj) {
return this.replace(SUBREGEX, function (match, key) {
return (obj[key]===undefined) ? '' : obj[key];
});
});
/**
* Returns a ISO-8601 Date-object build by the String's value.
* If the String-value doesn't match ISO-8601, `null` will be returned.
*
* ISO-8601 Date's are generated by JSON.stringify(), so it's very handy to be able to reconvert them.
*
* @example
* var birthday = '2010-02-10T14:45:30.000Z';
* birthday.toDate(); // --> Wed Feb 10 2010 15:45:30 GMT+0100 (CET)
*
* @method toDate
* @return {Date|null} the Date represented by the String's value or null when invalid
*/
String.toDate || (StringPrototype.toDate=function() {
return DATEPATTERN.test(this) ? new Date(this) : null;
});
/**
* Generated the string without any white-spaces at the start or end.
*
* @method trim
* @return {String} new String without leading and trailing white-spaces
*/
String.trim || (StringPrototype.trim=function() {
return this.replace(TRIMREGEX, '');
});
/**
* Generated the string without any white-spaces at the beginning.
*
* @method trimLeft
* @return {String} new String without leading white-spaces
*/
String.trimLeft || (StringPrototype.trimLeft=function() {
return this.replace(TRIM_LEFT_REGEX, '');
});
/**
* Generated the string without any white-spaces at the end.
*
* @method trimRight
* @return {String} new String without trailing white-spaces
*/
String.trimRight || (StringPrototype.trimRight=function() {
return this.replace(TRIM_RIGHT_REGEX, '');
});
/**
* Replaces search-characters by a replacement. Replaces only the firts occurence.
* Does not alter the String itself, but returns a new String with the replacement.
*
* @method replace
* @param search {String} the character(s) to be replaced
* @param replacement {String} the replacement
* @param [caseInsensitive=false] {Boolean} whether to do search case-insensitive
* @return {String} new String with the replacement
*/
replaceBKP = StringPrototype.replace;
StringPrototype.replace=function(search, replacement, caseInsensitive) {
var re;
if (caseInsensitive) {
re = new RegExp(search, 'i');
return this.replace(re, replacement);
}
else {
return replaceBKP.apply(this, arguments);
}
};
/**
* Replaces search-characters by a replacement. Replaces all occurences.
* Does not alter the String itself, but returns a new String with the replacements.
*
* @method replaceAll
* @param search {String} the character(s) to be replaced
* @param replacement {String} the replacement
* @param [caseInsensitive=false] {Boolean} whether to do search case-insensitive
* @return {String} new String with the replacements
*/
String.replaceAll || (StringPrototype.replaceAll=function(search, replacement, caseInsensitive) {
var re = new RegExp(search, 'g' + (caseInsensitive ? 'i' : ''));
return this.replace(re, replacement);
});
/**
* Validates if the String's value represents a valid emailaddress.
*
* @method validateEmail
* @return {Boolean} whether the String's value is a valid emailaddress.
*/
StringPrototype.validateEmail = function() {
return PATTERN_EMAIL.test(this);
};
/**
* Validates if the String's value represents a valid floated number.
*
* @method validateFloat
* @param [comma] {Boolean} whether to use a comma as decimal separator instead of a dot
* @return {Boolean} whether the String's value is a valid floated number.
*/
StringPrototype.validateFloat = function(comma) {
return comma ? PATTERN_FLOAT_COMMA.test(this) : PATTERN_FLOAT_DOT.test(this);
};
/**
* Validates if the String's value represents a hexadecimal color.
*
* @method validateHexaColor
* @param [alpha=false] {Boolean} whether to accept alpha transparancy
* @return {Boolean} whether the String's value is a valid hexadecimal color.
*/
StringPrototype.validateHexaColor = function(alpha) {
return alpha ? PATTERN_HEX_COLOR_ALPHA.test(this) : PATTERN_HEX_COLOR.test(this);
};
/**
* Validates if the String's value represents a valid integer number.
*
* @method validateNumber
* @return {Boolean} whether the String's value is a valid integer number.
*/
StringPrototype.validateNumber = function() {
return PATTERN_INTEGER.test(this);
};
/**
* Validates if the String's value represents a valid boolean.
*
* @method validateBoolean
* @return {Boolean} whether the String's value is a valid boolean.
*/
StringPrototype.validateBoolean = function() {
var length = this.length,
check;
if ((length<4) || (length>5)) {
return false;
}
check = this.toUpperCase();
return ((check==='TRUE') || (check==='FALSE'));
};
/**
* Validates if the String's value represents a valid Date.
*
* @method validateDate
* @return {Boolean} whether the String's value is a valid Date object.
*/
StringPrototype.validateDate = function() {
return DATEPATTERN.test(this);
};
/**
* Validates if the String's value represents a valid URL.
*
* @method validateURL
* @param [options] {Object}
* @param [options.http] {Boolean} to force matching starting with `http://`
* @param [options.https] {Boolean} to force matching starting with `https://`
* @return {Boolean} whether the String's value is a valid URL.
*/
StringPrototype.validateURL = function(options) {
var instance = this;
options || (options={});
if (options.http && options.https) {
return false;
}
return options.http ? PATTERN_URLHTTP.test(instance) : (options.https ? PATTERN_URLHTTPS.test(instance) : PATTERN_URL.test(instance));
};
}(String.prototype));
},{}],30:[function(require,module,exports){
module.exports=require(14)
},{}],31:[function(require,module,exports){
module.exports=require(15)
},{}],32:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":30,"./lib/window.console.js":31}],33:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":34,"./lib/timers.js":35}],34:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":26,"polyfill/polyfill-base.js":38}],35:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":38}],36:[function(require,module,exports){
module.exports=require(14)
},{}],37:[function(require,module,exports){
module.exports=require(15)
},{}],38:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":36,"./lib/window.console.js":37}],39:[function(require,module,exports){
module.exports=require(14)
},{}],40:[function(require,module,exports){
module.exports=require(15)
},{}],41:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":39,"./lib/window.console.js":40}],42:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":43,"./lib/timers.js":44}],43:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":26,"polyfill/polyfill-base.js":47}],44:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":47}],45:[function(require,module,exports){
module.exports=require(14)
},{}],46:[function(require,module,exports){
module.exports=require(15)
},{}],47:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":45,"./lib/window.console.js":46}],48:[function(require,module,exports){
var css = ".itsa-notrans, .itsa-notrans2,\n.itsa-notrans:before, .itsa-notrans2:before,\n.itsa-notrans:after, .itsa-notrans2:after {\n -webkit-transition: none !important;\n -moz-transition: none !important;\n -ms-transition: none !important;\n -o-transition: all 0s !important; /* opera doesn't support none */\n transition: none !important;\n}\n\n.itsa-no-overflow {\n overflow: hidden !important;\n}\n\n.itsa-invisible {\n position: absolute !important;\n}\n\n.itsa-invisible-relative {\n position: relative !important;\n}\n\n/* don't set visibility to hidden --> you cannot set a focus on those items */\n.itsa-invisible,\n.itsa-invisible *,\n.itsa-invisible-relative,\n.itsa-invisible-relative * {\n opacity: 0 !important;\n}\n\n/* don't set visibility to hidden --> you cannot set a focus on those items */\n.itsa-invisible-unfocusable,\n.itsa-invisible-unfocusable * {\n visibility: hidden !important;\n}\n\n.itsa-transparent {\n opacity: 0;\n}\n\n/* don't set visibility to hidden --> you cannot set a focus on those items */\n.itsa-hidden {\n opacity: 0 !important;\n position: absolute !important;\n left: -9999px !important;\n top: -9999px !important;\n z-index: -9;\n}\n\n.itsa-hidden * {\n opacity: 0 !important;\n}\n\n.itsa-no-display {\n display: none; !important;\n}\n\n.itsa-block {\n display: block !important;\n}\n\n.itsa-borderbox {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}"; (require("/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify"))(css); module.exports = css;
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],49:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":53,"js-ext/extra/hashmap.js":50,"polyfill/polyfill-base.js":59}],50:[function(require,module,exports){
module.exports=require(4)
},{}],51:[function(require,module,exports){
(function (global){
/**
*
* Pollyfils for often used functionality for Strings
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module js-ext
* @submodule lib/string.js
* @class String
*
*/
(function (global) {
"use strict";
var LightMap, Classes,
createHashMap = require('js-ext/extra/hashmap.js').createMap;
global._ITSAmodules || Object.protectedProp(global, '_ITSAmodules', createHashMap());
/*jshint boss:true */
if (LightMap=global._ITSAmodules.LightMap) {
/*jshint boss:false */
module.exports = LightMap; // LightMap was already created
return;
}
require('../lib/array.js');
require('../lib/object.js');
require('polyfill/lib/weakmap.js');
Classes = require("./classes.js");
global._ITSAmodules.LightMap = LightMap = Classes.createClass(
function() {
Object.protectedProp(this, '_array', []);
Object.protectedProp(this, '_map', new global.WeakMap());
},
{
each: function(fn, context) {
var instance = this,
array = instance._array,
l = array.length,
i = -1,
obj, value;
while (++i < l) {
obj = array[i];
value = instance.get(obj); // read from WeakMap
fn.call(context, value, obj, instance);
}
return instance;
},
some: function(fn, context) {
var instance = this,
array = instance._array,
l = array.length,
i = -1,
obj, value;
while (++i < l) {
obj = array[i];
value = instance.get(obj); // read from WeakMap
if (fn.call(context, value, obj, instance)) {
return true;
}
}
return false;
},
clear: function() {
var instance = this,
array = instance._array;
array.forEach(function(key) {
instance.delete(key, true);
});
array.length = 0;
},
has: function(object) {
return this._map.has(object);
},
get: function(key, fallback) {
return this._map.get(key, fallback);
},
set: function (key, value) {
var instance = this,
array = instance._array,
map = instance._map;
map.set(key, value);
array.contains(key) || array.push(key);
return instance;
},
size: function () {
return this._array.length;
},
'delete': function (key) {
var instance = this,
array = instance._array,
map = instance._map,
silent = arguments[1], // hidden feature used by `clear()`
returnValue = map.delete(key);
silent || array.remove(key);
return returnValue;
}
}
);
module.exports = LightMap;
}(typeof global !== 'undefined' ? global : /* istanbul ignore next */ this));
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"../lib/array.js":52,"../lib/object.js":53,"./classes.js":49,"js-ext/extra/hashmap.js":50,"polyfill/lib/weakmap.js":57}],52:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":59}],53:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":50,"polyfill/polyfill-base.js":59,"utils":60}],54:[function(require,module,exports){
"use strict";
/**
* Provides additional Promise-methods. These are extra methods which are not part of the PromiseA+ specification,
* But are all Promise/A+ compatable.
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
*
* @module js-ext
* @submodule lib/promise.s
* @class Promise
*/
require('polyfill');
var NAME = '[promise-ext]: ',
FUNCTION_EXPECTED = ' expects an array of function-references', // include leading space!
PROMISE_CHAIN = 'Promise.chain';
(function(PromisePrototype) {
/**
* Promise which can be put at the very end of a chain, even after .catch().
* Will invoke the callback function regardless whether the chain resolves or rejects.
*
* The argument of the callback will be either its fulfilled or rejected argument, but
* it is wisely not to handle it. The results should have been handled in an earlier step
* of the chain: .finally() basicly means you want to execute code after the chain, regardless
* whether it's resolved or rejected.
*
* **Note:** .finally() <u>does not return a Promise</u>: it should be used as the very last step of a Promisechain.
* If you need an intermediate method, you should take .thenFulfill().
*
* @method finally
* @param finallyback {Function} the callbackfunctio to be invoked.
* @return {Promise}
*/
PromisePrototype.finally = function (finallyback) {
console.log(NAME, 'finally');
return this.then(finallyback, finallyback);
};
/**
* Will always return a fulfilled Promise.
*
* Typical usage will be by making it part of a Promisechain: it makes the chain go
* into its fulfilled phase.
*
* @example
*
* promise1
* .then(promise2)
* .thenFulfill()
* .then(handleFulfilled, handleRejected) // handleFulfilled always gets invoked
* @method thenFulfill
* @param [response] {Object} parameter to pass through which overrules the original Promise-response.
* @return {Promise} Resolved Promise. `response` will be passed trough as parameter when set.
* When not set: in case the original Promise resolved, its parameter is passed through.
* in case of a rejection, no parameter will be passed through.
*/
PromisePrototype.thenFulfill = function (callback) {
console.log(NAME, 'thenFulfill');
return this.then(
function(r) {
return r;
},
function(r) {
return r;
}
).then(callback);
};
}(Promise.prototype));
/**
* Returns a Promise that always fulfills. It is fulfilled when ALL items are resolved (either fulfilled
* or rejected). This is useful for waiting for the resolution of multiple
* promises, such as reading multiple files in Node.js or making multiple XHR
* requests in the browser. Because -on the contrary of `Promise.all`- **finishAll** waits until
* all single Promises are resolved, you can handle all promises, even if some gets rejected.
*
* @method finishAll
* @param items {Any[]} an array of any kind of items, promises or not. If a value is not a promise,
* its transformed into a resolved promise.
* @return {Promise} A promise for an array of all the fulfillment items:
* <ul>
* <li>Fulfilled: o {Object}
* <ul>
* <li>fulfilled {Array} all fulfilled responses, any item that was rejected will have a value of `undefined`</li>
* <li>rejected {Array} all rejected responses, any item that was fulfilled will have a value of `undefined`</li>
* </ul>
* </li>
* <li>Rejected: this promise **never** rejects</li>
* </ul>
* @static
*/
Promise.finishAll = function (items) {
console.log(NAME, 'finishAll');
return new Promise(function (fulfill) {
// Array.isArray assumes ES5
Array.isArray(items) || (items=[items]);
var remaining = items.length,
length = items.length,
fulfilledresults = [],
rejectedresults = [],
i;
function oneDone(index, fulfilled) {
return function (value) {
fulfilled ? (fulfilledresults[index]=value) : (rejectedresults[index]=value);
remaining--;
if (!remaining) {
console.log(NAME, 'finishAll is fulfilled');
fulfill({
fulfilled: fulfilledresults,
rejected: rejectedresults
});
}
};
}
if (length < 1) {
console.warn(NAME, 'finishAll fulfilles immediately: no items');
return fulfill({
fulfilled: fulfilledresults,
rejected: rejectedresults
});
}
fulfilledresults.length = length;
rejectedresults.length = length;
for (i=0; i < length; i++) {
Promise.resolve(items[i]).then(oneDone(i, true), oneDone(i, false));
}
});
};
/**
* Returns a Promise which chains the function-calls. Like an automated Promise-chain.
* Invokes the functionreferences in a chain. You MUST supply function-references, it doesn't
* matter wheter these functions return a Promise or not. Any returnvalues are passed through to
* the next function.
*
* **Cautious:** you need to pass function-references, not invoke them!
* chainFns will invoke them when the time is ready. Regarding to this, there is a difference with
* using Promise.all() where you should pass invoked Promises.
*
* If one of the functions returns a Promise, the chain
* will wait its execution for this function to be resolved.
*
* If you need specific context or arguments: use Function.bind for these items.
* If one of the items returns a rejected Promise, by default: the whole chain rejects
* and following functions in the chain will not be invoked. When `finishAll` is set `true`
* the chain will always continue even with rejected Promises.
*
* Returning functionvalues are passed through the chain adding them as an extra argument
* to the next function in the chain (argument is added on the right)
*
* @example
* var a = [], p1, p2, p3;
* p1 = function(a) {
* return new Promise(function(resolve, reject) {
* I.later(function() {
* console.log('resolving promise p1: '+a);
* resolve(a);
* }, 1000);
* });
* };
* p2 = function(b, r) {
* var value = b+r;
* console.log('returning p2: '+value);
* return value;
* };
* p3 = function(c, r) {
* return new Promise(function(resolve, reject) {
* I.later(function() {
* var value = b+r;
* console.log('resolving promise p3: '+value);
* resolve(value);
* }, 1000);
* });
* };
* a.push(p1.bind(undefined, 100));
* a.push(p2.bind(undefined, 200));
* a.push(p3.bind(undefined, 300));
* Promise.chainFns(a).then(
* function(r) {
* console.log('chain resolved with '+r);
* },
* function(err) {
* console.log('chain-error '+err);
* }
* );
*
* @method chainFns
* @param funcs {function[]} an array of function-references
* @param [finishAll=false] {boolean} to force the chain to continue, even if one of the functions
* returns a rejected Promise
* @return {Promise}
* on success:
* o {Object} returnvalue of the laste item in the Promisechain
* on failure an Error object
* reason {Error}
* @static
*/
Promise.chainFns = function (funcs, finishAll) {
console.log(NAME, 'chainFns');
var handleFn, length, handlePromiseChain,
i = 0;
// Array.isArray assumes ES5
Array.isArray(funcs) || (funcs=[funcs]);
length = funcs.length;
handleFn = function() {
var nextFn = funcs[i],
promise;
if (typeof nextFn !== 'function') {
return Promise.reject(new TypeError(PROMISE_CHAIN+FUNCTION_EXPECTED));
}
promise = Promise.resolve(nextFn.apply(null, arguments));
// by using "promise.catch(function(){})" we return a resolved Promise
return finishAll ? promise.thenFulfill() : promise;
};
handlePromiseChain = function() {
// will loop until rejected, which is at destruction of the class
return handleFn.apply(null, arguments).then((++i<length) ? handlePromiseChain : undefined);
};
return handlePromiseChain();
};
/**
* Returns a Promise with 5 additional methods:
*
* promise.fulfill
* promise.reject
* promise.callback
* promise.setCallback
* promise.pending
*
* With Promise.manage, you get a Promise which is managable from outside, not inside as Promise A+ work.
* You can invoke promise.**callback**() which will invoke the original passed-in callbackFn - if any.
* promise.**fulfill**() and promise.**reject**() are meant to resolve the promise from outside, just like deferred can do.
*
* @example
* var promise = Promise.manage(
* function(msg) {
* alert(msg);
* }
* );
*
* promise.then(
* function() {
* // promise is fulfilled, no further actions can be taken
* }
* );
*
* setTimeout(function() {
* promise.callback('hey, I\'m still busy');
* }, 1000);
*
* setTimeout(function() {
* promise.fulfill();
* }, 2000);
*
* @method manage
* @param [callbackFn] {Function} invoked everytime promiseinstance.callback() is called.
* You may as weel (re)set this method atny time lare by using promise.setCallback()
* @return {Promise} with three handles: fulfill, reject and callback.
* @static
*/
Promise.manage = function (callbackFn) {
console.log(NAME, 'manage');
var fulfillHandler, rejectHandler, promise, finished;
promise = new Promise(function (fulfill, reject) {
fulfillHandler = fulfill;
rejectHandler = reject;
});
promise.fulfill = function (value) {
console.log(NAME, 'manage.fulfill');
finished = true;
fulfillHandler(value);
};
promise.reject = function (reason) {
console.log(NAME, 'manage.reject '+((typeof reason==='string') ? reason : reason && (reason.message || reason.description)));
finished = true;
rejectHandler(reason);
};
promise.pending = function () {
return !finished;
};
promise.callback = function () {
if (!finished && callbackFn) {
console.log(NAME, 'manage.callback is invoked');
callbackFn.apply(undefined, arguments);
}
};
promise.setCallback = function (newCallbackFn) {
callbackFn = newCallbackFn;
};
return promise;
};
},{"polyfill":59}],55:[function(require,module,exports){
module.exports=require(29)
},{}],56:[function(require,module,exports){
module.exports=require(14)
},{}],57:[function(require,module,exports){
(function (global){
// based upon https://gist.github.com/Gozala/1269991
(function (global) {
"use strict";
var defineNamespace, Name, guard;
if (!global.WeakMap) {
defineNamespace = function(object, namespace) {
/**
Utility function takes `object` and `namespace` and overrides `valueOf`
method of `object`, so that when called with a `namespace` argument,
`private` object associated with this `namespace` is returned. If argument
is different, `valueOf` falls back to original `valueOf` property.
**/
// Private inherits from `object`, so that `this.foo` will refer to the
// `object.foo`. Also, original `valueOf` is saved in order to be able to
// delegate to it when necessary.
var privates = Object.create(object),
base = object.valueOf;
Object.defineProperty(object, 'valueOf', {
value: function valueOf(value) {
// If `this` or `namespace` is not associated with a `privates` being
// stored we fallback to original `valueOf`, otherwise we return privates.
return ((value !== namespace) || (this !== object)) ? base.apply(this, arguments) : privates;
},
configurable: true
});
return privates;
};
Name = function() {
/**
Desugared implementation of private names proposal. API is different as
it's not possible to implement API proposed for harmony with in ES5. In
terms of provided functionality it supposed to be same.
http://wiki.ecmascript.org/doku.php?id=strawman:private_names
**/
var namespace = {};
return function name(object) {
var privates = object.valueOf(namespace);
return (privates !== object) ? privates : defineNamespace(object, namespace);
};
};
guard = function(key) {
/**
Utility function to guard WeakMap methods from keys that are not
a non-null objects.
**/
if (key !== Object(key)) {
throw new TypeError("value is not a non-null object");
}
return key;
};
global.WeakMap = function() {
/**
Implementation of harmony `WeakMaps`, in ES5. This implementation will
work only with keys that have configurable `valueOf` property (which is
a default for all non-frozen objects).
http://wiki.ecmascript.org/doku.php?id=harmony:weak_maps
**/
var privates = new Name();
return Object.freeze(Object.create(WeakMap.prototype, {
has: {
value: function has(object) {
return 'value' in privates(object);
},
configurable: true,
enumerable: false,
writable: true
},
get: {
value: function get(key, fallback) {
return privates(guard(key)).value || fallback;
},
configurable: true,
enumerable: false,
writable: true
},
set: {
value: function set(key, value) {
privates(guard(key)).value = value;
return this;
},
configurable: true,
enumerable: false,
writable: true
},
'delete': {
value: function set(key) {
return delete privates(guard(key)).value;
},
configurable: true,
enumerable: false,
writable: true
}
}));
};
}
}(typeof global !== 'undefined' ? global : /* istanbul ignore next */ this));
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],58:[function(require,module,exports){
module.exports=require(15)
},{}],59:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":56,"./lib/window.console.js":58}],60:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":61,"./lib/timers.js":62}],61:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":50,"polyfill/polyfill-base.js":65}],62:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":65}],63:[function(require,module,exports){
module.exports=require(14)
},{}],64:[function(require,module,exports){
module.exports=require(15)
},{}],65:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":63,"./lib/window.console.js":64}],66:[function(require,module,exports){
"use strict";
var merge = function (source, target) {
var keys = Object.keys(source),
l = keys.length,
i = -1,
key;
while (++i < l) {
key = keys[i];
target[key] = source[key];
}
},
hashMap = function(members) {
// important to set the prototype to `null` --> this will exclude any Object.prototype members
var obj = Object.create(null);
merge(members, obj);
return obj;
};
module.exports = {
createMap: hashMap
};
},{}],67:[function(require,module,exports){
"use strict";
/*
* Returns the right transform-property for the current environment.
*
* `transform`, `-webkit-transform`, `-moz-transform`, `-ms-transform`, `-o-transform` or `undefined` when not supported
*/
// CAUTIOUS: need a copy of hashmap --> we cannot use js-ext/extra/hashap.js for that would lead to circular references!
var createHashMap = require('../bin/local-hashmap.js').createMap;
module.exports = function (window) {
// NOTE: CANNOT use dependency to js-ext/lib/object.js --> would be circular!
if (!window._ITSAmodules) {
Object.defineProperty(window, '_ITSAmodules', {
configurable: false,
enumerable: false,
writable: false,
value: createHashMap() // `writable` is false means we cannot chance the value-reference, but we can change {} its members
});
}
if (window._ITSAmodules.Transition) {
return window._ITSAmodules.Transition; // Transition was already created
}
var DOCUMENT_STYLE = window.document.documentElement.style,
RANSITION = 'ransition',
TRANSITION = 't'+RANSITION,
VENDORS = ['-webkit-', '-moz-', '-ms-', '-o-'],
transition;
// Map transition properties to vendor-specific versions.
// One-off required for cssText injection.
if ((TRANSITION in DOCUMENT_STYLE) && (TRANSITION+'Property' in DOCUMENT_STYLE) &&
(TRANSITION+'Duration' in DOCUMENT_STYLE) && (TRANSITION+'TimingFunction' in DOCUMENT_STYLE) && (TRANSITION+'Delay' in DOCUMENT_STYLE)) {
transition = TRANSITION;
}
else {
VENDORS.some(function(val) { // then vendor specific
var property1 = val + TRANSITION,
property2 = val + 'T'+RANSITION;
((typeof DOCUMENT_STYLE[property1] !== 'undefined') || (typeof DOCUMENT_STYLE[property2] !== 'undefined')) && (transition=property1);
return transition;
});
}
window._ITSAmodules.Transition = transition || TRANSITION;
return transition;
};
},{"../bin/local-hashmap.js":66}],68:[function(require,module,exports){
"use strict";
// CAUTIOUS: need a copy of hashmap --> we cannot use js-ext/extra/hashap.js for that would lead to circular references!
var createHashMap = require('../bin/local-hashmap.js').createMap;
module.exports = function (window) {
// NOTE: CANNOT use dependency to js-ext/lib/object.js --> would be circular!
if (!window._ITSAmodules) {
Object.defineProperty(window, '_ITSAmodules', {
configurable: false,
enumerable: false,
writable: false,
value: createHashMap() // `writable` is false means we cannot chance the value-reference, but we can change {} its members
});
}
if (window._ITSAmodules.TransitionEnd) {
return window._ITSAmodules.TransitionEnd; // TransitionEnd was already created
}
var DOCUMENT_STYLE = window.document.documentElement.style,
transitions = {},
ransition = 'ransition',
transition = 't'+ransition,
end = 'end',
transitionEnd, t;
transitions[transition] = transition+end;
transitions['WebkitT'+ransition] = 'webkitT'+ransition+'End';
transitions['MozT'+ransition] = transition+end;
transitions['OT'+ransition] = 'o'+transition+end;
for (t in transitions) {
if (typeof DOCUMENT_STYLE[t] !== 'undefined') {
transitionEnd = transitions[t];
break;
}
}
window._ITSAmodules.TransitionEnd = transitionEnd;
return transitionEnd;
};
},{"../bin/local-hashmap.js":66}],69:[function(require,module,exports){
(function (global){
"use strict";
/*
* Returns the vendor-specific transform-property for the current environment.
*
* `transform`, `-webkit-transform`, `-moz-transform`, `-ms-transform`, `-o-transform` or `undefined` when not supported
*/
var toCamelCase = function(input) {
return input.replace(/-(.)/g, function(match, group) {
return group.toUpperCase();
});
},
UNDEFINED = 'undefined',
// CAUTIOUS: need a copy of hashmap --> we cannot use js-ext/extra/hashap.js for that would lead to circular references!
createHashMap = require('../bin/local-hashmap.js').createMap;
module.exports = function (window) {
// NOTE: CANNOT use dependency to js-ext/lib/object.js --> would be circular!
if (!window._ITSAmodules) {
Object.defineProperty(window, '_ITSAmodules', {
configurable: false,
enumerable: false,
writable: false,
value: createHashMap() // `writable` is false means we cannot chance the value-reference, but we can change {} its members
});
}
if (window._ITSAmodules.VendorCSS) {
return window._ITSAmodules.VendorCSS; // VendorCSS was already created
}
var DOCUMENT_STYLE = window.document.documentElement.style,
RUNNING_ON_NODE = (typeof global !== 'undefined') && (global.window!==window),
VENDORS = ['-webkit-', '-moz-', '-ms-', '-o-'],
vendorCSS;
window._ITSAmodules.VendorCSS = vendorCSS = {
generator: function(cssProperty) {
var vendorProperty;
if (cssProperty==='') {
return '';
}
if (!RUNNING_ON_NODE && !vendorCSS.cssProps[cssProperty]) {
if (typeof DOCUMENT_STYLE[cssProperty] !== UNDEFINED) {
vendorProperty = cssProperty;
}
else {
VENDORS.some(function(val) { // then vendor specific
var property = val + cssProperty,
propertyCamelCase = toCamelCase(property);
if ((typeof DOCUMENT_STYLE[property] !== UNDEFINED) || (typeof DOCUMENT_STYLE[propertyCamelCase] !== UNDEFINED)) {
vendorProperty = property;
}
return vendorProperty;
});
}
vendorCSS.cssProps[vendorProperty] = true;
}
return vendorProperty || cssProperty;
},
cssProps: {}
};
return vendorCSS;
};
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"../bin/local-hashmap.js":66}],70:[function(require,module,exports){
module.exports=require(14)
},{}],71:[function(require,module,exports){
module.exports=require(15)
},{}],72:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":70,"./lib/window.console.js":71}],73:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":74,"./lib/timers.js":75}],74:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":50,"polyfill/polyfill-base.js":78}],75:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":78}],76:[function(require,module,exports){
module.exports=require(14)
},{}],77:[function(require,module,exports){
module.exports=require(15)
},{}],78:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":76,"./lib/window.console.js":77}],79:[function(require,module,exports){
"use strict";
module.exports = function (window) {
require('./lib/sizes.js')(window);
};
},{"./lib/sizes.js":80}],80:[function(require,module,exports){
"use strict";
require('js-ext/lib/object.js');
var createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.WindowSizes) {
return; // WindowSizes was already created
}
window._ITSAmodules.WindowSizes = true;
var getScrollOffsets = function() {
var doc = window.document;
// this works for all browsers in non quircks-mode and only for IE9+:
if (window.pageXOffset!==undefined) { // do not "just" check for `window.pageXOffset` --> it could be `0`
return {
x: window.pageXOffset,
y: window.pageYOffset
};
}
// for IE (or any other browser) in standards mode
if (doc.compatMode === 'CSS1Compat') {
return {
x: doc.documentElement.scrollLeft,
y: doc.documentElement.scrollTop
};
}
// for browsers in quircks mode:
return {
x: doc.body.scrollLeft,
y: doc.body.scrollTop
};
},
getViewportSize = function() {
var doc = window.document;
// this works for all browsers in non quircks-mode and only for IE9+:
if (window.innerWidth!==undefined) { // do not "just" check for `window.innerWidth` --> it could be `0`
return {
w: window.innerWidth,
h: window.innerHeight
};
}
// for IE (or any other browser) in standards mode
if (doc.compatMode === 'CSS1Compat') {
return {
w: doc.documentElement.clientWidth,
h: doc.documentElement.clientHeight
};
}
// for browsers in quircks mode:
return {
w: doc.body.clientWidth,
h: doc.body.clientHeight
};
};
/**
* Gets the left-scroll offset of the window.
*
* @method getScrollLeft
* @return {Number} left-offset in pixels
* @since 0.0.1
*/
window.getScrollLeft = function() {
return getScrollOffsets().x;
};
/**
* Gets the top-scroll offset of the window.
*
* @method getScrollTop
* @return {Number} top-offset in pixels
* @since 0.0.1
*/
window.getScrollTop = function() {
return getScrollOffsets().y;
};
/**
* Gets the width of the window.
*
* @method getWidth
* @return {Number} width in pixels
* @since 0.0.1
*/
window.getWidth = function() {
return getViewportSize().w;
};
/**
* Gets the height of the window.
*
* @method getHeight
* @return {Number} width in pixels
* @since 0.0.1
*/
window.getHeight = function() {
return getViewportSize().h;
};
};
},{"js-ext/extra/hashmap.js":81,"js-ext/lib/object.js":82}],81:[function(require,module,exports){
module.exports=require(4)
},{}],82:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":81,"polyfill/polyfill-base.js":85,"utils":86}],83:[function(require,module,exports){
module.exports=require(14)
},{}],84:[function(require,module,exports){
module.exports=require(15)
},{}],85:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":83,"./lib/window.console.js":84}],86:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":87,"./lib/timers.js":88}],87:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":81,"polyfill/polyfill-base.js":91}],88:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":91}],89:[function(require,module,exports){
module.exports=require(14)
},{}],90:[function(require,module,exports){
module.exports=require(15)
},{}],91:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":89,"./lib/window.console.js":90}],92:[function(require,module,exports){
"use strict";
/**
* Exports `htmlToVNodes` which transforms html-text into vnodes.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* <br>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module vdom
* @submodule attribute-extractor
* @since 0.0.1
*/
require('js-ext/lib/string.js');
require('js-ext/lib/object.js');
require('polyfill');
var createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.AttributeExtractor) {
return window._ITSAmodules.AttributeExtractor; // AttributeExtractor was already created
}
var SUPPORT_INLINE_PSEUDO_STYLES = false, // current browsers don't support this. When tey do, set this value `true`
END_OF_VALUE = createHashMap({
';': true,
'}': true
}),
VENDOR_CSS = require('polyfill/extra/vendorCSS.js')(window),
generateVendorCSSProp = VENDOR_CSS.generator,
VENDOR_CSS_PROPERTIES = VENDOR_CSS.cssProps,
VENDOR_TRANSITION_PROPERTY = require('polyfill/extra/transition.js')(window), // DO NOT use TRANSITION-variable here --> browserify cannot deal this
_serializeTransition, _parseTransition, extractor;
window.document._supportInlinePseudoStyles = SUPPORT_INLINE_PSEUDO_STYLES;
_serializeTransition = function(transitionValue) {
// transitionValue should an Object !!
var serialized = '',
timingFunction, delay;
transitionValue.each(function(value, key) {
timingFunction = value.timingFunction;
delay = value.delay;
serialized += ', ' + key;
if (key!=='none') {
serialized += ' ' + value.duration+'s';
timingFunction && (serialized+=' ' + timingFunction);
delay && (serialized+=' ' + delay+'s');
}
});
return (serialized[0]===',') ? serialized.substr(2) : serialized;
};
_parseTransition = function(transitionValueSerialised) {
var parsed = {},
i, len, transitionItem, item, items, value, properties, item0, item1, item2, item3;
if (transitionValueSerialised) {
properties = transitionValueSerialised.split(',');
len = properties.length;
for (i=0; i<len; i++) {
items = properties[i].trim();
(items.indexOf(' ')!==-1) && items.replace(/' '/g, ' ');
item = items.split(' ');
item0 = item[0];
item1 = item[1];
item2 = item[2];
item3 = item[3];
if (item0.parsable()) {
// no key, but starting with a duration
item3 = item2;
item2 = item1;
item1 = item0;
item0 = 'all';
}
transitionItem = {};
(item0.toLowerCase()==='none') && (item0='none');
if (item0!=='none') {
transitionItem.duration = parseFloat(item1) || 0;
/*jshint boss:true */
if (value=item2) {
/*jshint boss:false */
// check if it is a Function, or a delayvalue
if (value.parsable()) {
transitionItem.delay = parseFloat(value);
}
else {
transitionItem.timingFunction = value;
(value=item3) && (transitionItem.delay = parseFloat(value));
}
}
}
// allways transform the css-property into a vendor-safe property:
VENDOR_CSS_PROPERTIES[item0] || (item0=generateVendorCSSProp(item0));
parsed[item0] = transitionItem;
}
}
return parsed;
};
extractor = window._ITSAmodules.AttributeExtractor = {
extractClass: function(classes) {
var attrClass = '',
classNames = {},
oneclass, len, i, character;
if (classes) {
oneclass = '';
len = classes.length;
for (i=0; i<len; i++) {
character = classes[i];
if (character===' ') {
if (oneclass!=='') {
classNames[oneclass] = true;
attrClass += ' '+oneclass;
oneclass = '';
}
}
else {
oneclass += character;
}
}
if (oneclass!=='') {
classNames[oneclass] = true;
attrClass += ' '+oneclass;
}
}
return {
attrClass: (attrClass==='') ? undefined : attrClass.substr(1),
classNames: classNames
};
},
extractStyle: function(styles) {
/* be aware you can encounter inline style like this:
style="{color: blue; background: white}
:visited {color: green}
:hover {background: yellow}
:visited:hover {color: purple}
OR
style="color: blue; background: white"
Also, you might encounter inline transform, which should be separated itself:
style="{color: blue; background: white; transform: translateX(10px) matrix(1.0, 2.0, 3.0, 4.0, 5.0, 6.0) translateY(5px);}
:visited {color: green}
:hover {background: yellow; transform: translateX(10px) matrix(1.0, 2.0, 3.0, 4.0, 5.0, 6.0) translateY(5px);}
:visited:hover {color: purple}
OR
style="color: blue; background: white; transform: translateX(10px) matrix(1.0, 2.0, 3.0, 4.0, 5.0, 6.0) translateY(5px);"
*/
var newStyles = {},
instance = this,
i, onlyElement, len, character, groupKey, key, value, insideValue, insideKey, hasValue, group;
if (styles) {
i = -1;
len = styles.length;
// first eliminate leading spaces
/*jshint noempty:true */
while ((++i<len) && (character=styles[i]) && (character===' ')) {}
/*jshint noempty:false */
// preview next character
character = styles[i];
onlyElement = (character && (character!=='{') && (character!==':'));
if (onlyElement) {
newStyles.element = {};
group = newStyles.element;
groupKey = 'element';
insideKey = true;
}
else {
groupKey = '';
}
// now process
key = '';
insideValue = false;
i--;
while ((++i<len) && (character=styles[i])) {
if (insideValue) {
hasValue = true;
if (END_OF_VALUE[character]) {
value = value.trim();
// in case `key` equals a variant of `transform`, but non-compatible with the current browser -->
// redefine it into a browser-compatible version:
VENDOR_CSS_PROPERTIES[key] || (key=generateVendorCSSProp(key));
// store the property:
if ((SUPPORT_INLINE_PSEUDO_STYLES || (groupKey==='element')) && (value.length>0)) {
group[key] = ((key===VENDOR_TRANSITION_PROPERTY) ? _parseTransition(value) : value);
}
key = '';
insideValue = false;
insideKey = (character===';');
insideKey || (groupKey='');
}
else {
value += character;
}
}
else if (insideKey) {
if (character===':'){
insideKey = false;
insideValue = true;
key = key.trim();
value = '';
}
else if (character==='}') {
insideKey = false;
groupKey = '';
}
else {
key += character;
}
}
else {
if (character==='{') {
groupKey = groupKey.trim();
(groupKey==='') && (groupKey='element');
group = newStyles[groupKey] = {};
insideKey = true;
key = '';
}
else {
groupKey += character;
}
}
}
if (insideValue) {
value = value.trim();
// in case `key` equals a variant of `transition`, but non-compatible with the current browser -->
// redefine it into a browser-compatible version:
VENDOR_CSS_PROPERTIES[key] || (key=generateVendorCSSProp(key));
// store the property:
if ((SUPPORT_INLINE_PSEUDO_STYLES || (groupKey==='element')) && (value.length>0)) {
group[key] = ((key===VENDOR_TRANSITION_PROPERTY) ? _parseTransition(value) : value);
}
}
}
if (!SUPPORT_INLINE_PSEUDO_STYLES) {
delete newStyles[':before'];
delete newStyles[':after'];
}
return {
attrStyle: hasValue && instance.serializeStyles(newStyles),
styles: newStyles
};
},
toTransitionObject: function(value) {
return _parseTransition(value);
},
serializeTransition: function(value) {
return _serializeTransition(value);
},
serializeStyles: function(styles) {
var serialized = '',
onlyElementStyle = ((styles.size()===1) && styles.element);
if (onlyElementStyle || !SUPPORT_INLINE_PSEUDO_STYLES) {
styles.element && styles.element.each(function(value, key) {
serialized += ' '+ key + ': ' + ((key===VENDOR_TRANSITION_PROPERTY) ? _serializeTransition(value) : value) + ';';
});
}
else {
styles.each(function(groupValue, groupKey) {
(groupKey==='element') || (serialized += ' '+groupKey+' ');
serialized += '{';
groupValue.each(function(value, key) {
serialized += key + ': ' + ((key===VENDOR_TRANSITION_PROPERTY) ? _serializeTransition(value) : value) + '; ';
});
serialized += '}';
});
(serialized==='{}') && (serialized='');
}
return (serialized[0]===' ') ? serialized.substr(1) : serialized;
}
};
return extractor;
};
},{"js-ext/extra/hashmap.js":50,"js-ext/lib/object.js":53,"js-ext/lib/string.js":55,"polyfill":72,"polyfill/extra/transition.js":67,"polyfill/extra/vendorCSS.js":69}],93:[function(require,module,exports){
"use strict";
/**
* Extends Array into an array with special utility-methods that can be applied upon its members.
* The membres should be vElement's
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* <br>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module vdom
* @submodule element-array
* @class ElementArray
* @since 0.0.1
*/
require('polyfill');
require('js-ext/lib/object.js');
var createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.ElementArray) {
return window._ITSAmodules.ElementArray; // ElementArray was already created
}
var forEach = function(list, method, args) {
var len = list.length,
i, element;
for (i=0; i<len; i++) {
element = list[i];
element[method].apply(element, args);
}
return list;
},
NodeListPrototype = window.NodeList.prototype,
HTMLCollectionPrototype = window.HTMLCollection.prototype,
arrayMethods = Object.getOwnPropertyNames(Array.prototype),
ElementArray,
ElementArrayMethods = {
/**
* For all vElements of the ElementArray:
* Appends a HtmlElement or text at the end of HtmlElement's innerHTML.
*
* @method append
* @param content {HtmlElement|HtmlElementList|String} content to append
* @param escape {Boolean} whether to insert `escaped` content, leading it into only text inserted
* @chainable
* @since 0.0.1
*/
append: function(/* content, escape */) {
return forEach(this, 'append', arguments);
},
/**
* For all vElements of the ElementArray:
* Sets the inline-style of the HtmlElement exactly to the specified `value`, overruling previous values.
* Making the HtmlElement's inline-style look like: style="value".
*
* This is meant for a quick one-time setup. For individually inline style-properties to be set, you can use `setInlineStyle()`.
*
* @method defineInlineStyle
* @param value {String} the style string to be set
* @chainable
* @since 0.0.1
*/
defineInlineStyle: function(/* value */) {
return forEach(this, 'defineInlineStyle', arguments);
},
/**
* For all vElements of the ElementArray:
* Checks whether the plugin is plugged in at ALL the HtmlElements of the NodeList/HTMLCollection.
* Checks whether all its attributes are set.
*
* @method isPlugged
* @param pluginClass {NodePlugin} The plugin that should be plugged. Needs to be the Class, not an instance!
* @return {Boolean} whether the plugin is plugged in
* @since 0.0.1
*/
isPlugged: function(NodePluginClass) {
return this.every(function(element) {
return element.isPlugged(NodePluginClass);
});
},
/**
* For all vElements of the ElementArray:
* Plugs in the plugin on the HtmlElement, and gives is special behaviour by setting the appropriate attributes.
*
* @method plug
* @param pluginClass {NodePlugin} The plugin that should be plugged. Needs to be the Class, not an instance!
* @param options {Object} any options that should be passed through when the class is instantiated.
* @chainable
* @since 0.0.1
*/
plug: function(/* NodePluginClass, options */) {
return forEach(this, 'plug', arguments);
},
/**
* For all vElements of the ElementArray:
* Prepends a HtmlElement or text at the start of HtmlElement's innerHTML.
*
* @method prepend
* @param content {HtmlElement|HtmlElementList|String} content to prepend
* @param [escape] {Boolean} whether to insert `escaped` content, leading it into only text inserted
* @chainable
* @since 0.0.1
*/
prepend: function(/* content, escape */) {
return forEach(this, 'prepend', arguments);
},
/**
* For all vElements of the ElementArray:
* Removes the attribute from the HtmlElement.
*
* Alias for removeAttribute().
*
* @method removeAttr
* @param attributeName {String}
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit
* @chainable
* @since 0.0.1
*/
removeAttr: function(/* attributeName, silent */) {
return forEach(this, 'removeAttr', arguments);
},
/**
* For all vElements of the ElementArray:
* Removes multiple attributes on the Element.
* The argument should be one ore more AttributeNames.
*
* @example
* instance.removeAttrs(['tabIndex', 'style']);
*
* @method removeAttrs
* @param attributeData {Array|String}
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit
* @chainable
* @since 0.0.1
*/
removeAttrs: function(/* attributeData, silent */) {
return forEach(this, 'removeAttrs', arguments);
},
/**
* For all vElements of the ElementArray:
* Removes a className from the HtmlElement.
*
* @method removeClass
* @param className {String} the className that should be removed.
* @chainable
* @since 0.0.1
*/
removeClass: function(/* className */) {
return forEach(this, 'removeClass', arguments);
},
/**
* For all vElements of the ElementArray:
* Removes data specified by `key`. When no arguments are passed, all node-data (key-value pairs) will be removed.
*
* @method removeData
* @param key {string} name of the key
* @chainable
* @since 0.0.1
*/
removeData: function(/* key */) {
return forEach(this, 'removeData', arguments);
},
/**
* For all vElements of the ElementArray:
* Removes a css-property (inline) out of the HtmlElement. Use camelCase.
*
* @method removeInlineStyle
* @param cssAttribute {String} the css-property to be removed
* @chainable
* @since 0.0.1
*/
removeInlineStyle: function(/* cssAttribute */) {
return forEach(this, 'removeInlineStyle', arguments);
},
/**
* For all vElements of the ElementArray:
* Removes the HtmlElement from the DOM.
*
* @method removeNode
* @since 0.0.1
*/
removeNode: function() {
var instance = this;
forEach(this, 'remove');
instance.length = 0;
return instance;
},
/**
* For all vElements of the ElementArray:
* Replaces the className of the HtmlElement with a new className.
* If the previous className is not available, the new className is set nevertheless.
*
* @method replaceClass
* @param prevClassName {String} the className to be replaced
* @param newClassName {String} the className to be set
* @param [force ] {Boolean} whether the new className should be set, even is the previous className isn't there
* @chainable
* @since 0.0.1
*/
replaceClass: function(/* prevClassName, newClassName, force */) {
return forEach(this, 'replaceClass', arguments);
},
/**
* For all vElements of the ElementArray:
* Replaces the HtmlElement with a new HtmlElement.
*
* @method replaceNode
* @param newHtmlElement {HtmlElement|String} the new HtmlElement
* @param [escape] {Boolean} whether to insert `escaped` content, leading it into only text inserted
* @since 0.0.1
*/
replaceNode: function(newHtmlElement, escape) {
var instance = this,
len = instance.length,
i;
for (i=len-1; i>=0; i--) {
instance[i] = instance[i].replace(newHtmlElement, escape);
// instance[i].replace(newHtmlElement, escape);
}
return instance;
},
/**
* For all vElements of the ElementArray:
* Sets the attribute on the HtmlElement with the specified value.
*
* Alias for setAttribute().
*
* @method setAttr
* @param attributeName {String}
* @param value {Any} the value that belongs to `key`
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit
* @chainable
* @since 0.0.1
*/
setAttr: function(/* attributeData, silent */) {
return forEach(this, 'setAttr', arguments);
},
/**
* For all vElements of the ElementArray:
* Sets multiple attributes on the Element with the specified value.
* The argument should be one ore more Objects with the properties: `name` and `value`
*
* @example
* instance.setAttrs([
* {name: 'tabIndex', value: '0'},
* {name: 'style', value: 'color: #000;'}
* ]);
*
* @method setAttrs
* @param attributeData {Array|Object}
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit
* @chainable
* @since 0.0.1
*/
setAttrs: function(/* attributeName, value, silent */) {
return forEach(this, 'setAttrs', arguments);
},
/**
* For all vElements of the ElementArray:
* Adds a class to the HtmlElement. If the class already exists it won't be duplicated.
*
* @method setClass
* @param className {String} className to be added
* @chainable
* @since 0.0.1
*/
setClass: function(/* className */) {
return forEach(this, 'setClass', arguments);
},
/**
* For all vElements of the ElementArray:
* Stores arbitary `data` at the HtmlElement. This has nothing to do with node-attributes whatsoever,
* it is just a way to bind any data to the specific Element so it can be retrieved later on with `getData()`.
*
* @method setData
* @param key {string} name of the key
* @param value {Any} the value that belongs to `key`
* @chainable
* @since 0.0.1
*/
setData: function(/* key, value */) {
return forEach(this, 'setData', arguments);
},
/**
* For all vElements of the ElementArray:
* Sets the content of the HtmlElement (innerHTML). Careful: only set content like this if you controll the data and
* are sure what is going inside. Otherwise XSS might occur. If you let the user insert, or insert right from a db,
* you might be better of using setContent().
*
* @method setHTML
* @param content {HtmlElement|HtmlElementList|String} content to append
* @chainable
* @since 0.0.1
*/
setHTML: function(/* content */) {
return forEach(this, 'setHTML', arguments);
},
/**
* For all vElements of the ElementArray:
* Sets a css-property (inline) out of the HtmlElement. Use camelCase.
*
* Note: no need to camelCase cssProperty: both `margin-left` as well as `marginLeft` are fine
*
* @method setStyle
* @param cssAttribute {String} the css-property to be set
* @param value {String} the css-value
* @chainable
* @since 0.0.1
*/
setInlineStyle: function(/* cssAttribute, value */) {
return forEach(this, 'setInlineStyle', arguments);
},
/**
* For all vElements of the ElementArray:
* Gets or sets the outerHTML of both the Element as well as the representing dom-node.
* Goes through the vdom, so it's superfast.
*
* Use this property instead of `outerHTML`
*
* Syncs with the DOM.
*
* @method setOuterHTML
* @param val {String} the new value to be set
* @chainable
* @since 0.0.1
*/
setOuterHTML: function(/* content */) {
return forEach(this, 'setOuterHTML', arguments);
},
/**
* For all vElements of the ElementArray:
* Sets the content of the HtmlElement. This is a safe way to set the content, because HTML is not parsed.
* If you do need to set HTML inside the node, use setHTML().
*
* @method setText
* @param content {HtmlElement|HtmlElementList|String} content to append. In case of HTML, it will be escaped.
* @param [escape] {Boolean} whether to insert `escaped` content, leading it into only text inserted
* @chainable
* @since 0.0.1
*/
setText: function(/* content */) {
return forEach(this, 'setText', arguments);
},
/**
* For all vElements of the ElementArray:
* Toggles the className of the Element.
*
* @method toggleClass
* @param className {String} the className that should be toggled
* @chainable
* @since 0.0.1
*/
toggleClass: function(/* className */) {
return forEach(this, 'toggleClass', arguments);
},
/**
* For all vElements of the ElementArray:
* Unplugs a NodePlugin from the HtmlElement.
*
* @method unplug
* @param pluginClass {NodePlugin} The plugin that should be unplugged. Needs to be the Class, not an instance!
* @chainable
* @since 0.0.1
*/
unplug: function(/* NodePluginClass */) {
return forEach(this, 'unplug', arguments);
}
};
// adding Array.prototype methods to NodeList.prototype
// Note: this might be buggy in IE8 and below: https://developer.mozilla.org/en-US/docs/Web/API/NodeList#Workarounds
arrayMethods.forEach(function(methodName) {
try {
NodeListPrototype[methodName] || (NodeListPrototype[methodName]=Array.prototype[methodName]);
HTMLCollectionPrototype[methodName] || (HTMLCollectionPrototype[methodName]=Array.prototype[methodName]);
}
catch(err) {
// some properties have only getters and cannot (and don't need) to be set
}
});
NodeListPrototype.merge(ElementArrayMethods);
HTMLCollectionPrototype.merge(ElementArrayMethods);
ElementArray = window._ITSAmodules.ElementArray = {
// unfortunatly, Object.create(Array.prototype) or Object.create([]) don't work as expected -->
// the bracket-notation isn't fucntional anymore:
// see http://www.bennadel.com/blog/2292-extending-javascript-arrays-while-keeping-native-bracket-notation-functionality.htm
createArray: function() {
var newArray = [];
newArray.merge(ElementArrayMethods);
return newArray;
}
};
return ElementArray;
};
},{"js-ext/extra/hashmap.js":50,"js-ext/lib/object.js":53,"polyfill":72}],94:[function(require,module,exports){
"use strict";
/**
* Provides several methods that override native document-methods to work with the vdom.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* <br>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module vdom
* @submodule extend-document
* @class document
* @since 0.0.1
*/
require('polyfill');
require('js-ext/lib/object.js');
require('js-ext/lib/string.js');
var createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.ExtendDocument) {
return; // ExtendDocument was already created
}
// prevent double definition:
window._ITSAmodules.ExtendDocument = true;
var DOCUMENT = window.document;
/**
* Returns a newly created TreeWalker object.
*
* The TreeWalker is life presentation of the dom. It gets updated when the dom changes.
*
* @method createTreeWalker
* @param root {Element} The root node at which to begin the NodeIterator's traversal.
* @param [whatToShow] {Number} Filter specification constants from the NodeFilter DOM interface, indicating which nodes to iterate over.
* You can use or sum one of the next properties:
* <ul>
* <li>window.NodeFilter.SHOW_ELEMENT</li>
* <li>window.NodeFilter.SHOW_COMMENT</li>
* <li>window.NodeFilter.SHOW_TEXT</li>
* </ul>
* @param [filter] {NodeFilter|function} An object implementing the NodeFilter interface or a function. See https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter
* @return {TreeWalker}
*/
DOCUMENT.createTreeWalker = function(root, whatToShow, filter) {
return root.createTreeWalker(whatToShow, filter);
};
/**
* Indicating whether an Element is inside the DOM.
*
* @method contains
* @param otherElement {Element}
* @param [insideItags=false] {Boolean} no deepsearch in iTags --> by default, these elements should be hidden
* @param [inspectProtectedNodes=false] {Boolean} no deepsearch in protected Nodes or iTags --> by default, these elements should be hidden
* @return {Boolean} whether the Element is inside the dom.
* @since 0.0.1
*/
DOCUMENT.contains = function(otherElement, insideItags, inspectProtectedNodes) {
return DOCUMENT.documentElement.contains(otherElement, insideItags, inspectProtectedNodes);
};
/**
* Gets an ElementArray of Elements, specified by the css-selector.
*
* @method getAll
* @param cssSelector {String} css-selector to match
* @param [insideItags=false] {Boolean} no deepsearch in iTags --> by default, these elements should be hidden
* @return {ElementArray} ElementArray of Elements that match the css-selector
* @since 0.0.1
*/
DOCUMENT.getAll = function(cssSelector, insideItags) {
return this.querySelectorAll(cssSelector, insideItags);
};
/**
* Gets one Element, specified by the css-selector. To retrieve a single element by id,
* you need to prepend the id-name with a `#`. When multiple Element's match, the first is returned.
*
* @method getElement
* @param cssSelector {String} css-selector to match
* @param [insideItags=false] {Boolean} no deepsearch in iTags --> by default, these elements should be hidden
* @return {Element|null} the Element that was search for
* @since 0.0.1
*/
DOCUMENT.getElement = function(cssSelector, insideItags) {
return ((cssSelector[0]==='#') && (cssSelector.indexOf(' ')===-1)) ? this.getElementById(cssSelector.substr(1)) : this.querySelector(cssSelector, insideItags);
};
/**
* Returns the Element matching the specified id.
*
* @method getElementById
* @param id {String} id of the Element
* @return {Element|null}
*
*/
DOCUMENT.getElementById = function(id, insideItags) {
return DOCUMENT.documentElement.getElementById(id, insideItags);
};
/**
* Returns the an Array with all itag-Elements
*
* @method getItags
* @return {Array}
*
*/
DOCUMENT.getItags = function() {
var instance = this,
findChildren;
// i-tag elements can only exists when the window.ITAGS are defined (by itags.core)
if (!window.ITAGS) {
return [];
}
if (instance._itagList) {
return instance._itagList;
}
// when not returned: it would be the first time --> we setup the current list
// the quickest way is by going through the vdom and inspect the tagNames ourselves:
findChildren = function(vnode) {
var vChildren = vnode.vChildren,
len = vChildren.length,
i, vChild;
for (i=0; i<len; i++) {
vChild = vChildren[i];
vChild.isItag && (DOCUMENT._itagList[DOCUMENT._itagList.length]=vChild.domNode);
findChildren(vChild);
}
};
Object.protectedProp(instance, '_itagList', []);
findChildren(instance.getElement('body').vnode);
return instance._itagList;
};
/**
* Returns the first Element that matches the CSS-selectors. You can pass one, or multiple CSS-selectors. When passed multiple,
* they need to be separated by a `comma`.
*
* @method querySelector
* @param selectors {String} CSS-selector(s) that should match
* @param [insideItags=false] {Boolean} no deepsearch in iTags --> by default, these elements should be hidden
* @return {Element}
*/
DOCUMENT.querySelector = function(selectors, insideItags) {
var docElement = DOCUMENT.documentElement;
if (docElement.matchesSelector(selectors)) {
return docElement;
}
return docElement.querySelector(selectors, insideItags);
};
/**
* Returns an ElementArray of all Elements that match the CSS-selectors. You can pass one, or multiple CSS-selectors. When passed multiple,
* they need to be separated by a `comma`.
*
* querySelectorAll is a snapshot of the dom at the time this method was called. It is not updated when changes of the dom are made afterwards.
*
* @method querySelectorAll
* @param selectors {String} CSS-selector(s) that should match
* @param [insideItags=false] {Boolean} no deepsearch in iTags --> by default, these elements should be hidden
* @return {ElementArray} non-life Array (snapshot) with Elements
*/
DOCUMENT.querySelectorAll = function(selectors, insideItags) {
var docElement = DOCUMENT.documentElement,
elements = docElement.querySelectorAll(selectors, insideItags);
docElement.matchesSelector(selectors) && elements.shift(docElement);
return elements;
};
/**
* Replaces the Element with a new Element.
*
* @method replace
* @param newHtmlElement {Element|String} the new element
* @param [escape] {Boolean} whether to insert `escaped` content, leading it into only the element having a TextNode as a child.
* @chainable
* @since 0.0.1
*/
DOCUMENT.replace = function(oldHtmlElement, newHtmlElement, escape) {
return oldHtmlElement.replace(newHtmlElement, escape);
};
/**
* Tests if an Element would be selected by the specified cssSelector.
* Alias for `matchesSelector()`
*
* @method test
* @param element {Element} The Element to test
* @param cssSelector {String} the css-selector to test against
* @return {Boolean} whether or not the node matches the selector
* @since 0.0.1
*/
DOCUMENT.test = function(element, cssSelector) {
return element.matches(cssSelector);
};
};
//--- declaration of properties ---------------------------
/**
* Returns the currently focused element, that is, the element that will get keystroke events if the user types any.
*
* @property activeElement
* @type Element
* @readOnly
*/
/**
* Returns an HTMLCollection with Elements of all of the `anchors` in the document that have a `name` specified (a[name]).
* For reasons of backwards compatibility, the returned set of anchors only contains those anchors created with the `name` attribute.
*
* `anchors` is a life presentation of the dom. The returned HTMLCollection gets updated when the dom changes.
*
* @property anchors
* @type HTMLCollection
* @readOnly
*/
/**
* Returns an HTMLCollection with Elements of all of the `applets` in the document.
*
* `applets` is a life presentation of the dom. The returned HTMLCollection gets updated when the dom changes.
*
* @property applets
* @type HTMLCollection
* @readOnly
*/
/**
* Returns the `body` or `frameset` Element of the current document, or null if no such element exists.
*
* @property body
* @type Element
* @readOnly
*/
/**
* Returns the `script`-Element whose script is currently being processed.
*
*
* @property currentScript
* @type Element
* @readOnly
*/
/**
* Returns the root-element (===`html`-Element) of the current document
*
* @property documentElement
* @type Element
* @readOnly
*/
/**
* Returns an HTMLCollection with Elements of all of the `embed`-elements in the document.
*
* `embeds` is a life presentation of the dom. The returned HTMLCollection gets updated when the dom changes.
*
* @property embeds
* @type HTMLCollection
* @readOnly
*/
/**
* Returns the firstChild element (===`html`-Element) of the current document
*
* @property firstChild
* @type Element
* @readOnly
*/
/**
* Returns an HTMLCollection with Elements of all of the `form`-elements in the document.
*
* `forms` is a life presentation of the dom. The returned HTMLCollection gets updated when the dom changes.
*
* @property forms
* @type HTMLCollection
* @readOnly
*/
/**
* Returns an HTMLCollection with Elements of all of the images in the document.
*
* `images` is a life presentation of the dom. The returned HTMLCollection gets updated when the dom changes.
*
* @property images
* @type HTMLCollection
* @readOnly
*/
/**
* Returns the lastChild element (===`html`-Element) of the current document
*
* @property lastChild
* @type Element
* @readOnly
*/
/**
* Returns an HTMLCollection with Elements of all of the of all `area`-Elements and `a`-Elements in a document with a value for the href attribute.
*
* `links` is a life presentation of the dom. The returned HTMLCollection gets updated when the dom changes.
*
* @property links
* @type HTMLCollection
* @readOnly
*/
/**
* Returns an HTMLCollection with Elements of all of the plugins (`object`- or `embed`-elements) in the document.
*
* `plugins` is a life presentation of the dom. The returned HTMLCollection gets updated when the dom changes.
*
* @property plugins
* @type HTMLCollection
* @readOnly
*/
/**
* Returns an HTMLCollection with Elements of all of the script-elements in the document.
*
* `scripts` is a life presentation of the dom. The returned HTMLCollection gets updated when the dom changes.
*
* @property scripts
* @type HTMLCollection
* @readOnly
*/
/**
* Returns an HTMLCollection with Elements of all of the style-elements in the document.
*
* `styleSheets` is a life presentation of the dom. The returned HTMLCollection gets updated when the dom changes.
*
* @property styleSheets
* @type HTMLCollection
* @readOnly
*/
/**
* Gets or sets the `title` of the document. That is, the `title`-Element within the `head`-Element
*
* @property title
* @type String
*/
//--- definition API of unmodified `document`-events ------
/**
* "online" event is fired on the <body> of each page when the browser switches between online and offline mode.
* The event is non-cancellable (you can't prevent the user from coming online, or going offline).
*
* @event online
*/
/**
* "offline" event is fired on the <body> of each page when the browser switches between online and offline mode.
* The event is non-cancellable (you can't prevent the user from coming online, or going offline).
*
* @event offline
*/
//--- definition API of unmodified `document`-methods ------
/**
* Adopts a node from an external document. The node and its subtree is removed from the document it's in (if any),
* and its ownerDocument is changed to the current document. The node can then be inserted into the current document.
*
* @method adoptNode
* @param externalNode {Node} The node from another document to be adopted.
* @return {Node} is the adopted node that can be used in the current document.
* The new node's parentNode is null, since it has not yet been inserted into the document tree.
*/
/**
* Adds a HtmlElement or DocumentFragment to the end of the `html`-element
*
* @method appendChild
* @param element {Element|DocumentFragment} the item to be appended
* @return {Element} the appended child.
*/
/**
* Creates a new attribute-node, and returns it.
*
* @method createAttribute
* @param name {String} The name of the attribute
* @return {AttributeNode}
*/
/**
* Creates a new Comment-node, and returns it.
*
* @method createComment
* @param data {String} The data to be added to the Comment.
* @return {CommentNode}
*/
/**
* Creates a new HtmlElement, and returns it.
*
* Don't use qualified names (like "html:a") with this method.
*
* @method createElement
* @param tagName {String} is a string that specifies the type of element to be created.
* The nodeName of the created element is initialized with the value of tagName.
* @return {HtmlElement}
*/
/**
* Returns a new NodeIterator object.
*
* The NodeIterator is a snapshot of the dom at the time this method was called. It is not updated when changes of the dom are made afterwards.
*
* @method createNodeIterator
* @param root {Element} The root node at which to begin the NodeIterator's traversal.
* @param [whatToShow] {Number} Filter specification constants from the NodeFilter DOM interface, indicating which nodes to iterate over.
* You can use or sum one of the next properties:
* <ul>
* <li>window.NodeFilter.SHOW_ELEMENT</li>
* <li>window.NodeFilter.SHOW_COMMENT</li>
* <li>window.NodeFilter.SHOW_TEXT</li>
* </ul>
* @param [filter] {NodeFilter|function} An object implementing the NodeFilter interface or a function. See https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter
* @return {NodeIterator}
*/
/**
* Returns a new Range object. See https://developer.mozilla.org/en-US/docs/Web/API/Range
*
* @method createRange
* @return {Range}
*/
/**
* Creates a new Text-node, and returns it.
*
* @method createTextNode
* @param data {String} The data to be added to the Text-node.
* @return {TextNode}
*/
/**
* Returns the Element from the document whose `elementFromPoint`-method is being called which is the topmost
* dom-Element which lies under the given point. To get a Element, specify the point via coordinates, in CSS pixels,
* relative to the upper-left-most point in the window or frame containing the document.
*
* @method elementFromPoint
* @param x {Number} x-coordinate to check, in CSS pixels relative to the upper-left corner of the document
* @param y {Number} y-coordinate to check, in CSS pixels relative to the upper-left corner of the document
* @return {Element} the matching Element
*/
/**
* Enables the style sheets matching the specified name in the current style sheet set,
* and disables all other style sheets (except those without a title, which are always enabled).
*
* @method enableStyleSheetsForSet
* @param name {String} The name of the style sheets to enable. All style sheets with a title that match this name will be enabled,
* while all others that have a title will be disabled. Specify an empty string for the name parameter
* to disable all alternate and preferred style sheets (but not the persistent style sheets; that is, those with no title attribute).
*/
/**
* Returns an ElementArray of all Elements that match their classes with the supplied `classNames` argument.
* To match multiple different classes, separate them with a `comma`.
*
* getElementsByClassName is life presentation of the dom. The returned ElementArray gets updated when the dom changes.
*
* NOTE: it is highly recomended to use `document.getAll` because that method takes advantage of the vdom.
*
*
* @method getElementsByClassName
* @param classNames {String} the classes to search for
* @return {ElementArray} life Array with Elements
*/
/**
* Returns an ElementArray of all Elements that match their `name`-attribute with the supplied `name` argument.
*
* getElementsByName is life presentation of the dom. The returned ElementArray gets updated when the dom changes.
*
* NOTE: it is highly recomended to use `document.getAll` because that method takes advantage of the vdom.
*
* @method getElementsByName
* @param name {String} the property of name-attribute to search for
* @return {ElementArray} life Array with Elements
*/
/**
* Returns an ElementArray of all Elements that match their `name`-attribute with the supplied `name` argument.
*
* getElementsByTagName is life presentation of the dom. The returned ElementArray gets updated when the dom changes.
*
* NOTE: it is highly recomended to use `document.getAll` because that method takes advantage of the vdom.
*
* @method getElementsByTagName
* @param tagNames {String} the tags to search for
* @return {ElementArray} life Array with Elements
*/
/**
* Returns a selection object representing the range of text selected by the user.
*
* Is also available on the `window`-object.
*
* @method getSelection
* @return {Selection} A Selection object. When cast to string, either by adding empty quotes "" or using .toString, this object is the text selected.
*/
/**
* Returns a Boolean value indicating whether the document or any element inside the document has focus.
*
* @method hasFocus
* @return {Boolean} whether the document or any element inside the document has focus.
*/
/**
* Creates a copy of a node from an external document that can be inserted into the current document.
*
* @method importNode
* @param externalNode {Node} The node from another document to be adopted.
* @param deep {Boolean} Whether the descendants of the imported node need to be imported.
* @return {Node} The new node that is imported into the document.
* The new node's parentNode is null, since it has not yet been inserted into the document tree.
*/
/**
* Inserts `newElement` before `referenceElement`.
*
* @method insertBefore
* @param newElement {Element} The newElement to insert
* @param referenceElement {Element} The Element before which newElement is inserted.
* @return {Element} the Element being inserted (equals newElement)
*/
/**
* Removes a child node from the DOM.
*
* @method removeChild
* @param child {Element} the Element to be removed from the DOM
* @return {Element} a reference to the removed child node
*/
/**
* Replaces one child-element of its parent element with a new child-element.
*
* @method replaceChild
* @param newChild {Element} the new element to replace oldChild. If it already exists in the DOM, it is first removed.
* @param oldChild {Element} The existing child to be replaced.
* @return {Element} is the replaced node. This is the same Element as oldChild.
*/
//--- definition API of unmodified `document`-properties ------
/**
* Returns the character encoding of the current document.
*
* @property characterSet
* @readOnly
*/
/**
* Indicates whether the document is rendered in Quirks mode or Standards mode. Its value is either:
* <ul>
* <li>`BackCompat` if the document is in quirks mode</li>
* <li>`CSS1Compat` if the document is in no-quirks (also known as `standards`) mode or limited-quirks (also known as `almost standards`) mode.</li>
* </ul>
*
* @property compatMode
* @readOnly
*/
/**
* Returns the MIME type that the document is being rendered as. This may come from HTTP headers or other sources of MIME information,
* and might be affected by automatic type conversions performed by either the browser or extensions.
*
* @property contentType
* @readOnly
*/
/**
* Returns the Document Type Declaration (DTD) associated with current document. The returned object implements the DocumentType interface.
* Use DOMImplementation.createDocumentType() to create a DocumentType.
*
* @property doctype
* @readOnly
*/
/**
* Returns string URI of the HTML document. Same as `document.URL`.
*
* Note: HTML documents have a document.URL property which returns the same value. Unlike URL, documentURI is available on all types of documents.
*
* @property documentURI
* @type String
* @readOnly
*/
/**
* Controls whether the entire document is editable. Its value should be either "off" or "on".
*
* @property designMode
* @type String
* @default "off"
*/
/**
* Gets the domain portion of the origin of the current document.
*
* Setter will fail, because the same origin policy needs to persist.
*
* @property domain
* @type String
* @readOnly
*/
/**
* Returns a DOMImplementation object associated with the current document.
*
* @property implementation
* @type DOMImplementation
* @readOnly
*/
/**
* Returns a string containing the date and time on which the current document was last modified.
* If you want a Date-object, you need to transform lastModified into a Date object: `modifyDate = new Date(document.lastModified);`
*
* @property lastModified
* @type String
* @readOnly
*/
/**
* Returns the last enabled style sheet set; this property's value changes whenever the document.selectedStyleSheetSet property is changed.
*
* @property lastStyleSheetSet
* @type String
* @readOnly
*/
/**
* returns a Location object, which contains information about the URL of the document and provides methods for changing that URL and loading another URL.
*
* Though Document.location is a read-only Location object, you can also assign a DOMString to it. This means that you can work with document.location
* as if it were a string in most cases: document.location = 'http://www.example.com' is a synonym of document.location.href = 'http://www.example.com'.
*
* To retrieve just the URL as a string, the read-only document.URL property can also be used.
*
* See more about the `Location` object: https://developer.mozilla.org/en-US/docs/Web/API/Location
*
* @property location
* @type Location
* @readOnly
*/
/**
* Returns the preferred style sheet set as set by the page author. This is determined from the order of style sheet declarations and the Default-Style HTTP header.
*
* @property preferredStyleSheetSet
* @type String
*/
/**
* Returns "loading" while the document is loading, "interactive" once it is finished parsing but still loading sub-resources,
* and "complete" once it has loaded.
*
* @property readyState
* @type String
* @readOnly
*/
/**
* Returns the URI of the page that linked to this page.
*
* @property referrer
* @type String
* @readOnly
*/
/**
* Indicates the name of the style sheet set that's currently in use. See more about Stylesheets: https://developer.mozilla.org/en-US/docs/Web/API/Stylesheet
* Setting the value of this property is equivalent to calling document.enableStyleSheetsForSet() with the value of currentStyleSheetSet,
* then setting the value of lastStyleSheetSet to that value as well.
*
* @property selectedStyleSheetSet
* @type String
*/
/**
* Returns string URL of the HTML document. Same as `document.documentURI`
*
* Note: HTML documents have a document.URL property which returns the same value. Unlike URL, documentURI is available on all types of documents.
*
* @property URL
* @type String
* @readOnly
*/
},{"js-ext/extra/hashmap.js":50,"js-ext/lib/object.js":53,"js-ext/lib/string.js":55,"polyfill":72}],95:[function(require,module,exports){
(function (global){
"use strict";
/**
* Provides several methods that override native Element-methods to work with the vdom.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* <br>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module vdom
* @submodule extend-element
* @class Element
* @since 0.0.1
*/
require('../css/element.css');
require('js-ext/lib/object.js');
require('js-ext/lib/string.js');
require('js-ext/lib/promise.js');
require('polyfill');
var createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.ExtendElement) {
return; // ExtendElement was already created
}
// prevent double definition:
window._ITSAmodules.ExtendElement = true;
var NAME = '[extend-element]: ',
ElementArray = require('./element-array.js')(window),
domNodeToVNode = require('./node-parser.js')(window),
htmlToVNodes = require('./html-parser.js')(window),
vNodeProto = require('./vnode.js')(window),
NS = require('./vdom-ns.js')(window),
RUNNING_ON_NODE = (typeof global !== 'undefined') && (global.window!==window),
TRANSITION = 'transition',
TRANSFORM = 'transform',
BROWSERS_SUPPORT_PSEUDO_TRANS = false, // set true as soon as they do
SUPPORTS_PSEUDO_TRANS = null, // is a life check --> is irrelevant as long BROWSERS_SUPPORT_PSEUDO_TRANS === false
VENDOR_CSS = require('polyfill/extra/vendorCSS.js')(window),
generateVendorCSSProp = VENDOR_CSS.generator,
VENDOR_CSS_PROPERTIES = VENDOR_CSS.cssProps,
VENDOR_TRANSFORM_PROPERTY = generateVendorCSSProp(TRANSFORM),
VENDOR_TRANSITION_PROPERTY = require('polyfill/extra/transition.js')(window), // DO NOT use TRANSITION-variable here --> browserify cannot deal this
EV_TRANSITION_END = require('polyfill/extra/transitionend.js')(window),
_BEFORE = ':before',
_AFTER = ':before',
extractor = require('./attribute-extractor.js')(window),
UTILS = require('utils'),
later = UTILS.later,
async = UTILS.async,
idGenerator = UTILS.idGenerator,
DOCUMENT = window.document,
nodeids = NS.nodeids,
arrayIndexOf = Array.prototype.indexOf,
POSITION = 'position',
ITSA_ = 'itsa-',
BLOCK = ITSA_+'block',
BORDERBOX = ITSA_+'borderbox',
NO_TRANS = ITSA_+'notrans',
NO_TRANS2 = NO_TRANS+'2', // needed to prevent removal of NO_TRANS when still needed `notrans`
INVISIBLE = ITSA_+'invisible',
INVISIBLE_RELATIVE = INVISIBLE+'-relative',
INVISIBLE_UNFOCUSABLE = INVISIBLE+'-unfocusable',
HIDDEN = ITSA_+'hidden',
LEFT = 'left',
TOP = 'top',
BORDER = 'border',
WIDTH = 'width',
HEIGHT = 'height',
STRING = 'string',
CLASS = 'class',
STYLE = 'style',
OVERFLOW = 'overflow',
SCROLL = 'scroll',
BORDER_LEFT_WIDTH = BORDER+'-left-'+WIDTH,
BORDER_RIGHT_WIDTH = BORDER+'-right-'+WIDTH,
BORDER_TOP_WIDTH = BORDER+'-top-'+WIDTH,
BORDER_BOTTOM_WIDTH = BORDER+'-bottom-'+WIDTH,
NUMBER = 'number',
PX = 'px',
SET = 'set',
TOGGLE = 'toggle',
REPLACE = 'replace',
REMOVE = 'remove',
_STARTSTYLE = '_startStyle',
setupObserver,
SIBLING_MATCH_CHARACTER = createHashMap({
'+': true,
'~': true
}),
NON_CLONABLE_STYLES = createHashMap({
absolute: true,
hidden: true,
block: true
}),
// CSS_PROPS_TO_CALCULATE should not be a hashMap, but an object --> we need to iterate with .each
CSS_PROPS_TO_CALCULATE = { // http://www.w3.org/TR/css3-transitions/#animatable-css
backgroundColor: true,
backgroundPositionX: true,
backgroundPositionY: true,
borderBottomColor: true,
borderBottomWidth: true,
borderLeftColor: true,
borderLeftWidth: true,
borderRightColor: true,
borderRightWidth: true,
borderTopColor: true,
borderTopWidth: true,
borderSpacing: true,
bottom: true,
clip: true,
color: true,
fontSize: true,
fontWeight: true,
height: true,
left: true,
letterSpacing: true,
lineHeight: true,
marginBottom: true,
marginTop: true,
marginLeft: true,
marginRight: true,
maxHeight: true,
maxWidth: true,
minHeight: true,
minWidth: true,
opacity: true,
outlineColor: true,
outlineWidth: true,
paddingBottom: true,
paddingTop: true,
paddingLeft: true,
paddingRight: true,
right: true,
textIndent: true,
textShadow: true,
verticalAlign: true,
// visibility: true, DO NOT use visibility!
width: true,
wordSpacing: true,
zIndex: true
},
// CSS_PROPS_TO_CALCULATE.transform is set later on by the vendor specific transform-property
htmlToVFragments = function(html, nameSpace, allowScripts) {
var vnodes = htmlToVNodes(html, vNodeProto, nameSpace, null, null, allowScripts),
len = vnodes.length,
_cleanupStyle = false,
vnode, i, bkpAttrs, bkpVChildNodes, scriptContent, _scripts, _scriptVNodes, scriptVNode;
for (i=0; i<len; i++) {
vnode = vnodes[i];
if (vnode.nodeType===1) {
(vnode.tag==='STYLE') && (_cleanupStyle=true);
if (vnode.tag==='SCRIPT') {
scriptContent = (vnode.attrs && vnode.attrs.src) ? vnode.attrs.src : vnode.vChildNodes[0].text;
// check if the parent has this script set:
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
_scripts || (_scripts=[]);
_scripts[_scripts.length] = scriptContent;
_scriptVNodes || (_scriptVNodes=[]);
_scriptVNodes[_scriptVNodes.length] = vnode;
// CREATE INNER TEXTNODE
scriptVNode = Object.create(vNodeProto);
scriptVNode.nodeType = 3;
scriptVNode.domNode = DOCUMENT.createTextNode(scriptContent);
// create circular reference:
scriptVNode.domNode._vnode = scriptVNode;
scriptVNode.text = scriptContent;
scriptVNode.vParent = vnode;
vnode.vChildNodes = [scriptVNode];
}
// same tag --> only update what is needed
bkpAttrs = vnode.attrs;
bkpVChildNodes = vnode.vChildNodes;
// reset, to force creation of inner domNodes:
vnode.attrs = {};
vnode.vChildNodes = [];
// next: sync the vnodes:
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpVChildNodes);
}
else {
vnode.domNode.nodeValue = vnode.text;
}
}
return {
isFragment: true,
vnodes: vnodes,
_scripts: _scripts,
_scriptVNodes: _scriptVNodes,
_cleanupStyle: _cleanupStyle
};
},
toCamelCase = function(input) {
input || (input='');
return input.replace(/-(.)/g, function(match, group) {
return group.toUpperCase();
});
},
fromCamelCase = function(input) {
input || (input='');
return input.replace(/[a-z]([A-Z])/g, function(match, group) {
return match[0]+'-'+group.toLowerCase();
});
},
getVendorCSS = function(cssProperties) {
var uniqueProps = {},
i, len, prop, safeProperty, uniqueSafeProperty;
len = cssProperties.length;
for (i=len-1; i>=0; i--) {
// set the right property, but also dedupe when there are multiple same vendor-properties
prop = cssProperties[i];
safeProperty = prop.property;
if (safeProperty) {
safeProperty = fromCamelCase(safeProperty);
uniqueSafeProperty = safeProperty+'#'+prop.pseudo;
VENDOR_CSS_PROPERTIES[safeProperty] || (safeProperty=generateVendorCSSProp(safeProperty));
if (uniqueProps[uniqueSafeProperty]) {
cssProperties.splice(i, 1);
}
else {
uniqueProps[uniqueSafeProperty] = true;
prop.property = safeProperty;
}
}
}
return cssProperties;
},
vendorSupportsPseudoTrans = function() {
// DO NOT CHANGE THIS FUNCTION!
// it does exactly what it should do:
// Sarari seems to support speudo transmisions, however it calculates css-properties wrong when they are 'undefined'
// within a specific node, while the 'non-pseudo' is defined.
// This would lead into a wrong calculation (too many) of the number of expected transitionend-events
// Thus, this feature is disabled in some specific browsers
if (SUPPORTS_PSEUDO_TRANS) {
return SUPPORTS_PSEUDO_TRANS;
}
var cssnode, node, nodeParent;
DOCUMENT.body.prepend('<style id="vendorSupportsPseudoTrans_css" type="text/css">#vendorSupportsPseudoTransParent {background-color:#F00;} #vendorSupportsPseudoTrans {background-color:#00F;}</style>');
DOCUMENT.body.prepend('<div id="vendorSupportsPseudoTransParent"><div id="vendorSupportsPseudoTrans"></div></div>');
node = DOCUMENT.getElement('#vendorSupportsPseudoTrans');
nodeParent = DOCUMENT.getElement('#vendorSupportsPseudoTransParent');
cssnode = DOCUMENT.getElement('#vendorSupportsPseudoTrans_css');
SUPPORTS_PSEUDO_TRANS = node.getStyle('background-color')!==node.getStyle('background-color', ':before');
cssnode.remove();
nodeParent.remove();
return SUPPORTS_PSEUDO_TRANS;
},
getTransPromise = function(node, hasTransitionedStyle, removalPromise, afterTransEventsNeeded, transitionProperties, maxtranstime) {
var promise, fallback;
afterTransEventsNeeded || (afterTransEventsNeeded=1);
if (hasTransitionedStyle) {
promise = new window.Promise(function(fulfill) {
var afterTrans = function(e) {
var finishedProperty = e.propertyName,
index;
if (finishedProperty) {
// some browsers support this feature: now we can exactly determine what promise to fulfill
delete transitionProperties[finishedProperty];
// in case of shorthand properties (such as padding) allmost all browsers
// fire multiple detailed events (http://www.smashingmagazine.com/2013/04/26/css3-transitions-thank-god-specification/).
// therefore, we also must delete the shortcut property when a detailed property gets fired:
index = finishedProperty.indexOf('-');
if (index!==-1) {
finishedProperty = finishedProperty.substr(0, index);
delete transitionProperties[finishedProperty];
}
// now fulfill when empty:
if (transitionProperties.isEmpty()) {
fallback.cancel();
console.log('Transition fulfilled');
node.removeEventListener(EV_TRANSITION_END, afterTrans, true);
fulfill();
}
}
else {
// in cae the browser doesn't support e.propertyName, we need to countdown:
if (--afterTransEventsNeeded<=0) {
fallback.cancel();
node.removeEventListener(EV_TRANSITION_END, afterTrans, true);
console.log('Transition fulfilled by counting nr. of endTransition events');
fulfill();
}
}
};
if (EV_TRANSITION_END===undefined) {
// no transition supported
console.log('No endTransition events supported: transition fulfilled');
fulfill();
}
else {
node.addEventListener(EV_TRANSITION_END, afterTrans, true);
fallback = later(function(){
console.log('Transition fulfilled by timer');
fulfill();
}, maxtranstime*1000+50); // extra 50 ms, after all, it is a fallback, we don't want it to take over the original end-transition-events
}
});
removalPromise && (promise=window.Promise.finishAll([promise, removalPromise]));
}
else {
promise = removalPromise || window.Promise.resolve();
}
return promise;
},
getClassTransPromise = function(node, method, className, extraData1, extraData2) {
// first. check if the final node has a transitioned property.
// If not, then return as fulfilled. If so, then check for all the transitioned properties,
// if there is any who changes its calculated value. If not, then return as fulfilled. If so, then setup
// the evenlistener
var resolvedPromise = window.Promise.resolve(),
currentInlineCSS = [],
finalInlineCSS = [],
finalNode, getsTransitioned, originalCSS, finalCSS, transPropertiesElement, transPropertiesBefore, transPropertiesAfter, bkpFreezedData1, endIntermediate,
promise, finalCSS_before, finalCSS_after, transpromise, manipulated, getCurrentProperties, currentProperties, bkpNodeData, bkpFreezed, cleanup,
originalCSS_before, originalCSS_after, searchTrans, generateInlineCSS, finalStyle, unFreeze, freezedExtraData1, startStyle, unfreezePromise,
transprops, transpropsBefore, transpropsAfter, time1, time2;
time1 = Date.now();
bkpNodeData = idGenerator('bkpNode');
bkpFreezed = idGenerator('bkpFreezed');
bkpFreezedData1 = idGenerator('bkpFreezedData1');
if ((method===TOGGLE) && !extraData1) {
// because -when toggling- the future current node-class might have been changed:
freezedExtraData1 = !node.hasClass(className);
}
unFreeze = function(options) {
var bkpFreezedStyle = node.getData(bkpFreezed),
finish = options && options.finish,
cancel = options && options.cancel,
transitioned = !finish;
bkpFreezedData1 = node.getData(bkpFreezedData1);
if (bkpFreezedStyle!==undefined) {
if (finish || cancel) {
node.setClass(NO_TRANS2);
}
else {
node.setData(_STARTSTYLE, bkpFreezedStyle);
}
if (!cancel) {
switch(method) {
case SET:
unfreezePromise = node.setClass(className, transitioned);
break;
case REPLACE:
unfreezePromise = node.replaceClass(extraData1, className, extraData2, transitioned);
break;
case REMOVE:
unfreezePromise = node.removeClass(className, transitioned);
break;
case TOGGLE:
unfreezePromise = node.toggleClass(className, (bkpFreezedData1===undefined) ? extraData1 : bkpFreezedData1, transitioned);
break;
}
}
else {
unfreezePromise = resolvedPromise;
}
async(function() {
node.removeData(bkpFreezed);
node.removeData(bkpFreezedData1);
});
if (finish || cancel) {
finalStyle = finalNode.getAttr(STYLE);
node.setAttr(STYLE, finalStyle);
later(function() { // not just async --> it seems we need more time
node.removeClass(NO_TRANS2);
}, 50);
unfreezePromise = resolvedPromise;
}
return unfreezePromise;
}
return promise;
};
resolvedPromise.cancel = function() { /* NOOP for compatibility */ };
resolvedPromise.freeze = function() { return window.Promise.resolve(0); /* compatibility */ };
resolvedPromise.unfreeze = unFreeze;
resolvedPromise.finish = function() { /* NOOP for compatibility */ };
if (EV_TRANSITION_END===undefined) {
return resolvedPromise;
}
cleanup = function() {
// we manipulate the classes as they should be, before returning the original inline style:
// all without Promise-return!
if (!promise.cancelled && !promise.frozen) {
switch(method) {
case SET:
node.setClass(className);
break;
case REPLACE:
node.replaceClass(extraData1, className, extraData2);
break;
case REMOVE:
node.removeClass(className);
break;
case TOGGLE:
node.toggleClass(className, extraData1);
break;
}
}
// last transitionrun: reset the inline css:
finalStyle = finalNode.getAttr(STYLE);
if (!promise.frozen) {
node.removeData(bkpFreezed);
node.removeData(bkpFreezedData1);
node.setClass(NO_TRANS2);
node.setAttr(STYLE, finalStyle);
}
else {
node.setData(bkpFreezed, finalStyle);
}
node.removeData(bkpNodeData);
finalNode.remove();
async(function() {
node.removeClass(NO_TRANS2);
promise.fulfill();
});
};
endIntermediate = function(type) {
if (!promise.isFulfilled) {
manipulated = true;
node.setData(bkpFreezedData1, freezedExtraData1);
currentProperties = getCurrentProperties(node, transprops);
node.setClass(NO_TRANS2);
node.setInlineStyles(currentProperties, false, true);
if (BROWSERS_SUPPORT_PSEUDO_TRANS) {
node.setInlineStyles(getCurrentProperties(node, transpropsBefore, ':before'), false, true);
node.setInlineStyles(getCurrentProperties(node, transpropsAfter, ':after'), false, true);
}
// also force to set the style on the node outside the vdom --> by forcing this
// we won't run into the situation where the vdom doesn't change the dom because the style didn';'t change:
node._setAttribute(STYLE, node.getAttr(STYLE));
Object.defineProperty(promise, 'isFulfilled', {
configurable: false,
enumerable: false,
writable: false,
value: true
});
Object.defineProperty(promise, type, {
configurable: false,
enumerable: false,
writable: false,
value: true
});
if (transpromise) {
transpromise.reject(); // prevent transitionpromise to set its own final values after finishing
}
else {
// in case `transpromise` wasn't setup yet:
async(function() {
transpromise.reject(); // prevent transitionpromise to set its own final values after finishing
});
}
}
time2 || (time2=Date.now());
return new window.Promise(function(resolve) {
async(function() {
resolve(time2-time1);
});
});
};
searchTrans = function(CSS1, CSS2, transProperties) {
var allTrans = !!transProperties.all,
searchObject = allTrans ? CSS_PROPS_TO_CALCULATE : transProperties,
transprops = {};
searchObject.each(function(transProp, key) {
// transProp will always be a vendor-specific property already
key = toCamelCase(key);
if (CSS1[key]!==CSS2[key]) {
transprops[key] = true;
}
});
return (transprops.size()>0) ? transprops : null;
};
generateInlineCSS = function(group, transProperties, CSS1, CSS2) {
transProperties.each(function(value, key) {
var prop1 = {property: key, value: CSS1[key]},
prop2 = {property: key, value: CSS2[key]};
if (group) {
prop1.pseudo = group;
prop2.pseudo = group;
}
currentInlineCSS[currentInlineCSS.length] = prop1;
finalInlineCSS[finalInlineCSS.length] = prop2;
});
};
getCurrentProperties = function(node, transProperties, group) {
var props = [],
styles = window.getComputedStyle(node, group);
transProperties.each(function(value, property) {
// if property is vendor-specific transition, or transform, than we reset it to the current vendor
props.push({
property: property,
value: styles[toCamelCase(property)],
pseudo: group
});
});
return props;
};
finalNode = node.cloneNode(true);
finalNode.setClass(NO_TRANS2);
finalNode.setClass(INVISIBLE_UNFOCUSABLE);
node.setData(bkpNodeData, finalNode);
startStyle = node.getData(_STARTSTYLE);
if (startStyle!==undefined) {
finalNode.setAttr(STYLE, startStyle);
node.removeData(_STARTSTYLE);
}
switch(method) {
case SET:
finalNode.setClass(className);
break;
case REPLACE:
finalNode.replaceClass(extraData1, className, extraData2);
break;
case REMOVE:
finalNode.removeClass(className);
break;
case TOGGLE:
finalNode.toggleClass(className, extraData1);
break;
}
// insert in the dom, to make its style calculatable:
DOCUMENT.body.append(finalNode);
// check the css-property `transition`
finalNode.removeClass(NO_TRANS2);
transPropertiesElement = finalNode.getStyle(TRANSITION);
transPropertiesBefore = finalNode.getStyle(TRANSITION, _BEFORE);
transPropertiesAfter = finalNode.getStyle(TRANSITION, _AFTER);
finalNode.setClass(NO_TRANS2);
getsTransitioned = false;
if (!RUNNING_ON_NODE && ((transPropertiesElement.size()>0) || (transPropertiesBefore.size()>0) || (transPropertiesAfter.size()>0))) {
// when code comes here, there are one or more properties that can be transitioned
// check if their values differ from the original node
originalCSS = window.getComputedStyle(node);
originalCSS_before = window.getComputedStyle(node, _BEFORE);
originalCSS_after = window.getComputedStyle(node, _AFTER);
finalCSS = window.getComputedStyle(finalNode);
finalCSS_before = window.getComputedStyle(finalNode, _BEFORE);
finalCSS_after = window.getComputedStyle(finalNode, _AFTER);
/*jshint boss:true */
if (transprops=searchTrans(originalCSS, finalCSS, transPropertiesElement)) {
/*jshint boss:false */
getsTransitioned = true;
generateInlineCSS(null, transprops, originalCSS, finalCSS);
}
if (BROWSERS_SUPPORT_PSEUDO_TRANS && vendorSupportsPseudoTrans()) {
/*jshint boss:true */
if (transpropsBefore=searchTrans(originalCSS_before, finalCSS_before, transPropertiesBefore)) {
/*jshint boss:false */
getsTransitioned = true;
generateInlineCSS(_BEFORE, transpropsBefore, originalCSS_before, finalCSS_before);
}
/*jshint boss:true */
if (transpropsAfter=searchTrans(originalCSS_after, finalCSS_after, transPropertiesAfter)) {
/*jshint boss:false */
getsTransitioned = true;
generateInlineCSS(_AFTER, transpropsAfter, originalCSS_after, finalCSS_after);
}
}
}
if (getsTransitioned) {
// to force the transitioned items to work, we will set their calculated inline values for both at the start as well
// as on the end of the transition.
// set the original css inline:
promise = window.Promise.manage();
promise.finally(function() {
time2 || (time2=Date.now());
});
node.setClass(NO_TRANS2);
node.setInlineStyles(currentInlineCSS, false, true);
async(function() {
if (!manipulated) {
node.removeClass(NO_TRANS2);
transpromise = node.setInlineStyles(finalInlineCSS, true, true);
transpromise.finally(function() {
// async `setAttr` --> only fulfill when the DOM has been updated
async(function() {
cleanup();
});
});
}
});
promise.cancel = function() {
return endIntermediate('cancelled');
};
promise.freeze = function() {
return endIntermediate('frozen');
};
promise.finish = function() {
return endIntermediate('finished');
};
promise.unfreeze = unFreeze;
return promise;
}
else {
switch(method) {
case SET:
node.setClass(className);
break;
case REPLACE:
node.replaceClass(extraData1, className, extraData2);
break;
case REMOVE:
node.removeClass(className);
break;
case TOGGLE:
node.toggleClass(className, extraData1);
break;
}
node.removeData(bkpNodeData);
finalNode.remove();
}
return resolvedPromise;
},
classListProto = {
add: function(className) {
// we do not use the property className, but setAttribute, because setAttribute can be hacked by other modules like `vdom`
// note: `this` is the returned object which is NOT the Elementinstance
var thisobject = this,
element = thisobject.element,
doSet = function(cl) {
var clName = element.vnode.attrs[CLASS] || '';
// we do not use the property className, but setAttribute, because setAttribute can be hacked by other modules like `vdom`
thisobject.contains(cl) || (element.setAttribute(CLASS, clName+((clName.length>0) ? ' ' : '') + cl));
};
if (typeof className === STRING) {
doSet(className);
}
else if (Array.isArray(className)) {
className.forEach(doSet);
}
},
remove: function(className) {
var element = this.element,
doRemove = function(cl) {
var clName = element.vnode.attrs[CLASS] || '',
regexp = new RegExp('(?:^|\\s+)' + cl + '(?:\\s+|$)', 'g');
// we do not use the property className, but setAttribute, because setAttribute can be hacked by other modules like `vdom`
// note: `this` is the returned object which is NOT the Elementinstance
element.setAttribute(CLASS, clName.replace(regexp, ' ').trim());
};
if (typeof className === STRING) {
doRemove(className);
}
else if (Array.isArray(className)) {
className.forEach(doRemove);
}
(element.vnode.attrs[CLASS]==='') && element.removeAttr(CLASS);
},
toggle: function(className, forceState) {
// we do not use the property className, but setAttribute, because setAttribute can be hacked by other modules like `vdom`
// note: `this` is the returned object which is NOT the Elementinstance
var thisobject = this,
doToggle = function(cl) {
if (typeof forceState === 'boolean') {
forceState ? thisobject.add(cl) : thisobject.remove(cl);
}
else {
thisobject.contains(cl) ? thisobject.remove(cl) : thisobject.add(cl);
}
};
if (typeof className === STRING) {
doToggle(className);
}
else if (Array.isArray(className)) {
className.forEach(doToggle);
}
},
contains: function(className) {
// we do not use the property className, but setAttribute, because setAttribute can be hacked by other modules like `vdom`
// note: `this` is the returned object which is NOT the Elementinstance.
// May be an Array of classNames, which all needs to be present.
return this.element.vnode.hasClass(className);
},
item: function(index) {
var items = this.element.vnode.attrs['class'].split(' ');
return items[index];
},
_init: function(element) {
this.element = element;
}
},
treeWalkerProto = {
_init: function(element, whatToShow, filter) {
var instance = this;
if (typeof filter !== 'function') {
// check if it is a NodeFilter-object
filter && filter.acceptNode && (filter=filter.acceptNode);
}
(typeof filter==='function') || (filter=null);
instance.vNodePointer = element.vnode;
instance._root = element;
whatToShow || (whatToShow=-1); // -1 equals NodeFilter.SHOW_ALL
(whatToShow===-1) && (whatToShow=133);
instance._whatToShow = whatToShow; // making it accessable for the getter `whatToShow`
instance._filter = filter; // making it accessable for the getter `filter`
},
_match: function(vnode, forcedVisible) {
var whatToShow = this._whatToShow,
filter = this._filter,
showElement = ((whatToShow & 1)!==0),
showComment = ((whatToShow & 128)!==0),
showText = ((whatToShow & 4)!==0),
typeMatch = (showElement && (vnode.nodeType===1)) || (showComment && (vnode.nodeType===8)) || (showText && (vnode.nodeType===3)),
visibleMatch = !forcedVisible || (window.getComputedStyle(vnode.domNode).display!=='none'),
funcMatch = filter ? filter(vnode.domNode) : true;
return typeMatch && visibleMatch && funcMatch;
},
firstChild: function() {
var instance = this,
foundVNode = instance.vNodePointer.vFirstChild;
while (foundVNode && !instance._match(foundVNode)) {
foundVNode = foundVNode.vNext;
}
foundVNode && (instance.vNodePointer=foundVNode);
return foundVNode && foundVNode.domNode;
},
lastChild: function() {
var instance = this,
foundVNode = instance.vNodePointer.vLastChild;
while (foundVNode && !instance._match(foundVNode)) {
foundVNode = foundVNode.vPrevious;
}
foundVNode && (instance.vNodePointer=foundVNode);
return foundVNode && foundVNode.domNode;
},
nextNode: function() {
var instance = this,
foundVNode = instance.vNodePointer.vNext;
while (foundVNode && !instance._match(foundVNode, true)) {
foundVNode = foundVNode.vNext;
}
foundVNode && (instance.vNodePointer=foundVNode);
return foundVNode && foundVNode.domNode;
},
nextSibling: function() {
var instance = this,
foundVNode = instance.vNodePointer.vNext;
while (foundVNode && !instance._match(foundVNode)) {
foundVNode = foundVNode.vNext;
}
foundVNode && (instance.vNodePointer=foundVNode);
return foundVNode && foundVNode.domNode;
},
parentNode: function() {
var instance = this,
foundVNode = instance.vNodePointer.vParent;
(foundVNode!==instance._root) && (instance.vNodePointer=foundVNode);
return foundVNode && foundVNode.domNode;
},
previousNode: function() {
var instance = this,
foundVNode = instance.vNodePointer.vPrevious;
while (foundVNode && !instance._match(foundVNode, true)) {
foundVNode = foundVNode.vPrevious;
}
foundVNode && (instance.vNodePointer=foundVNode);
return foundVNode && foundVNode.domNode;
},
previousSibling: function() {
var instance = this,
foundVNode = instance.vNodePointer.vPrevious;
while (foundVNode && !instance._match(foundVNode)) {
foundVNode = foundVNode.vPrevious;
}
foundVNode && (instance.vNodePointer=foundVNode);
return foundVNode && foundVNode.domNode;
}
};
require('window-ext')(window);
Object.defineProperties(treeWalkerProto, {
'currentNode': {
get: function() {
return this.vNodePointer.domNode;
}
},
'filter': {
get: function() {
return this._filter;
}
},
'root': {
get: function() {
return this._root;
}
},
'whatToShow': {
get: function() {
return this._whatToShow;
}
}
});
// NOTE: `vnode` should be a property of Node, NOT Element
/**
* Reference to the vnode-object that represents the Node
*
* (will autogenerate a vnode, should it not exists)
*
* @for Node
* @property vnode
* @type vnode
* @since 0.0.1
*/
Object.defineProperty(window.Node.prototype, 'vnode', {
get: function() {
var instance = this,
vnode = instance._vnode,
parentNode, parentVNode, index;
if (!vnode) {
vnode = instance._vnode = domNodeToVNode(instance);
parentNode = instance.parentNode;
// parentNode.vnode will be an existing vnode, because it runs through the same getter
// it will only be `null` if `html` is not virtualised
parentVNode = parentNode && parentNode.vnode;
if (parentVNode) {
// set the vnode at the right position of its children:
index = arrayIndexOf.call(parentNode.childNodes, instance);
vnode._moveToParent(parentVNode, index);
}
}
return vnode;
},
set: function() {} // NOOP but needs to be there, otherwise we could clone any domNodes
});
CSS_PROPS_TO_CALCULATE[VENDOR_TRANSFORM_PROPERTY] = true;
CSS_PROPS_TO_CALCULATE[generateVendorCSSProp(TRANSFORM+'-origin')] = true;
CSS_PROPS_TO_CALCULATE[generateVendorCSSProp('perspective')] = true;
(function(ElementPrototype) {
/**
* Determines the number of transitionend-events there will occur
* @method _getEvtTransEndCount
* @private
* @since 0.0.1
*/
ElementPrototype._getEvtTransEndCount = function(cssProperties) {
var transitions = this.getStyle(TRANSITION),
timing = {},
duration, delay, time;
transitions.each(function(transition) {
if (!cssProperties || (cssProperties[transition.property])) {
duration = transition.duration || 0;
delay = transition.delay || 0;
time = (duration+delay);
timing[time] = true;
}
});
return timing.size();
};
/**
* Returns cascaded "transition" style of all transition-properties. `Cascaded` means: the actual present style,
* the way it is visible (calculated through the DOM-tree).
*
* Note1: When "transition" is set inline, ONLY inline transtition is active!
* Thus, if parentNode has "transition: width 2s" and inline has "transition: height 3s", then the transition
* will be "transition: height 3s" --> returning "undefined" for transitionProperty=width.
* Note2: in case of "transition: all" --> these values will be returned for every "transitionProperty" (even when querying "width")
*
* @method _getTransitionAll
* @param transitionProperty {String} transform property that is queried, f.e. "width", or "all"
* @param [pseudo] {String} to query pseudo-element, fe: `:before` or `:first-line`
* @return {Object} the transition-object, with the properties:
* <ul>
* <li>duration {Number}</li>
* <li>timingFunction {String}</li>
* <li>delay {Number}</li>
* </ul>
* @private
* @since 0.0.1
*/
ElementPrototype._getTransitionAll = function(pseudo) {
var instance = this,
transProperty, transDuration, transTimingFunction, transDelay, transPropertySplitted, property,
transitions, transDurationSplitted, transTimingFunctionSplitted, transDelaySplitted, i, len, duration;
// first look at inline transition:
transitions = instance.getInlineTransition(null, pseudo);
if (transitions) {
return transitions;
}
// no inline transitions over here --> calculate using getStyle
transitions = {};
transProperty = instance.getStyle(VENDOR_TRANSITION_PROPERTY+'Property', pseudo);
transDuration = instance.getStyle(VENDOR_TRANSITION_PROPERTY+'Duration', pseudo);
transTimingFunction = instance.getStyle(VENDOR_TRANSITION_PROPERTY+'TimingFunction', pseudo);
transDelay = instance.getStyle(VENDOR_TRANSITION_PROPERTY+'Delay', pseudo);
if (transProperty) {
transPropertySplitted = transProperty && transProperty.split(',');
transDurationSplitted = transDuration.split(',');
transTimingFunctionSplitted = transTimingFunction.split(',');
transDelaySplitted = transDelay.split(',');
len = transPropertySplitted.length;
for (i=0; i<len; i++) {
property = transPropertySplitted[i];
duration = transTimingFunctionSplitted[i];
if ((property!=='none') && (duration!=='0s')) {
if (property!=='all') {
property = VENDOR_CSS_PROPERTIES[property] || generateVendorCSSProp(property);
}
transitions[property] = {
duration: parseFloat(transDurationSplitted[i]),
timingFunction: duration,
delay: parseFloat(transDelaySplitted[i])
};
}
}
}
return transitions;
};
/**
* Inserts a system-HTMLElement. These are child-Elements, just like other children, but they have a slightly different behaviour:
*
* <ul>
* <li>They get inserted as the first of the children</li>
* <li>They don't show up when querying</li>
* <li>They retain whenever new content is set for the parent-Element</li>
* </ul>
*
* System-Elements are useful f.e. when you want to add special features to an Element, like scrolling or resizing. You can add `helper-elements`
* (the system-elements) which keep hidden (protected) and retain whenever the Element changes his content.
*
* @method addSystemElement
* @param content {Element|ElementArray|String} content to append
* @param [escape] {Boolean} whether to insert `escaped` content, leading it into only text inserted
* @param [silent=true] {Boolean} prevent node-mutation events by the Event-module to emit --> defaults TRUE
* @return {Element} the created Element (or the last when multiple)
*/
ElementPrototype.addSystemElement = function(content, escape, silent) {
var instance = this,
vChildNodes = instance.vnode.vChildNodes,
len = vChildNodes.length,
systemElement, refElement, i;
(typeof silent === 'boolean') || (silent=true);
for (i=0; (i<len) && !refElement; i++) {
vChildNodes[i]._systemNode || (refElement=vChildNodes[i].domNode);
}
systemElement = refElement ? instance.prepend(content, escape, refElement, silent) : instance.append(content, escape, refElement, silent);
systemElement.vnode._systemNode = true;
systemElement.setAttr('is', 'system-node', true);
return systemElement;
};
/**
* Appends an Element or an Element's string-representation at the end of Element's innerHTML, or before the `refElement`.
*
* @for Element
* @method append
* @param content {Element|ElementArray|String} content to append
* @param [escape] {Boolean} whether to insert `escaped` content, leading it into only text inserted
* @param [refElement] {Element} reference Element where the content should be appended
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit
* @param [allowScripts=false] {Boolean} whether scripts are allowed --> these should be defined with `xscript` instead of `script`
* @return {Element} the created Element (or the last when multiple)
* @since 0.0.1
*/
ElementPrototype.append = function(content, escape, refElement, silent, allowScripts) {
var instance = this,
vnode = instance.vnode,
prevSuppress = DOCUMENT._suppressMutationEvents || false,
i, len, item, createdElement, vnodes, vRefElement, _scripts, scriptcontent,
doAppend = function(oneItem) {
escape && (oneItem.nodeType===1) && (oneItem=DOCUMENT.createTextNode(oneItem.getOuterHTML()));
createdElement = refElement ? vnode._insertBefore(oneItem.vnode, refElement.vnode) : vnode._appendChild(oneItem.vnode);
};
silent && DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
vnode._noSync()._normalizable(false);
if (refElement && (vnode.vChildNodes.indexOf(refElement.vnode)!==-1)) {
vRefElement = refElement.vnode.vNext;
refElement = vRefElement && vRefElement.domNode;
}
(typeof content===STRING) && (content=htmlToVFragments(content, vnode.ns, allowScripts));
if (content.isFragment) {
vnodes = content.vnodes;
len = vnodes.length;
for (i=0; i<len; i++) {
doAppend(vnodes[i].domNode);
}
/*jshint boss:true */
if (_scripts=content._scripts) {
/*jshint boss:false */
len = _scripts.length;
vnode._scripts || (vnode._scripts=[]);
for (i=0; i<len; i++) {
scriptcontent = _scripts[i];
if (!vnode._scripts.contains(scriptcontent)) {
vnode._scripts[vnode._scripts.length] = scriptcontent;
}
}
}
// in case a style-tag was added, we need to cleanup double definitions:
content._cleanupStyle && vnode._cleanupStyle();
}
else if (Array.isArray(content)) {
len = content.length;
for (i=0; i<len; i++) {
item = content[i];
doAppend(item);
}
}
else {
doAppend(content);
}
vnode._normalizable(true)._normalize();
silent && DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(prevSuppress);
return createdElement;
};
/**
* Adds a node to the end of the list of childNodes of a specified parent node.
*
* @method appendChild
* @param content {Element|ElementArray|String} content to append
* @param [escape] {Boolean} whether to insert `escaped` content, leading it into only text inserted
* @return {Element} the Element that was appended
*/
ElementPrototype._appendChild = ElementPrototype.appendChild;
ElementPrototype.appendChild = function(domNode, escape) {
return this.append(domNode, escape);
};
/**
* Returns a duplicate of the node. Use cloneNode(true) for a `deep` clone.
*
* @method cloneNode
* @param [deep] {Boolean} whether to perform a `deep` clone: with all descendants
* @return {Element} a clone of this Element
* @since 0.0.1
*/
ElementPrototype._cloneNode = ElementPrototype.cloneNode;
ElementPrototype.cloneNode = function(deep) {
var instance = this,
vnode = instance.vnode,
plugins = [],
cloned = instance._cloneNode(deep),
cloneData = function(srcVNode, targetVNode) {
if (srcVNode._data) {
Object.protectedProp(targetVNode, '_data', {});
targetVNode._data.merge(srcVNode._data);
}
},
updatePlugins = function(srcVNode, targetVNode) {
targetVNode.attrs && targetVNode.attrs.each(function(value, key) {
var pluginName;
if (key.substr(0, 7)==='plugin-') {
pluginName = key.substr(7);
// remove and reset the plugin with shallowcloned modeldata
// needs to be scheduled --> when deep cloned the full node needs t be build up first
// otherwise we could get doube rendered nodes.
plugins[plugins.length] = {
domNode: targetVNode.domNode,
pluginName: pluginName,
model: srcVNode.domNode._plugin[pluginName].model.shallowClone()
};
}
});
},
cloneDeepData = function(srcVNode, targetVNode) {
var srcVChildren = srcVNode.vChildren,
targetVChildren = targetVNode.vChildren,
len = srcVChildren.length,
i, childSrcVNode, childTargetVNode;
for (i=0; i<len; i++) {
childSrcVNode = srcVChildren[i];
childTargetVNode = targetVChildren[i];
cloneData(childSrcVNode, childTargetVNode);
updatePlugins(childSrcVNode, childTargetVNode);
childSrcVNode.hasVChildren() && cloneDeepData(childSrcVNode, childTargetVNode);
}
},
i, len, PluginClass, pluginDef;
cloned.vnode = domNodeToVNode(cloned);
cloneData(vnode, cloned.vnode);
updatePlugins(vnode, cloned.vnode);
// if deep, then we need to merge _data of all deeper nodes
deep && vnode.hasVChildren() && cloneDeepData(vnode, cloned.vnode);
len = plugins.length;
for (i=0; i<len; i++) {
pluginDef = plugins[i];
PluginClass = window._ITSAPlugins[pluginDef.pluginName];
pluginDef.domNode.unplug(PluginClass);
pluginDef.domNode.plug(PluginClass, null, pluginDef.model);
}
return cloned;
};
/**
* Compares the position of the current node against another node in any other document.
*
* Returnvalues are a composition of the following bitwise values:
* <ul>
* <li>Node.DOCUMENT_POSITION_DISCONNECTED === 1 (one of the Elements is not part of the dom)</li>
* <li>Node.DOCUMENT_POSITION_PRECEDING === 2 (this Element comes before otherElement)</li>
* <li>Node.DOCUMENT_POSITION_FOLLOWING === 4 (this Element comes after otherElement)</li>
* <li>Node.DOCUMENT_POSITION_CONTAINS === 8 (otherElement trully contains -not equals- this Element)</li>
* <li>Node.DOCUMENT_POSITION_CONTAINED_BY === 16 (Element trully contains -not equals- otherElement)</li>
* </ul>
*
* @method compareDocumentPosition
* @param otherElement {Element}
* @return {Number} A bitmask, use it this way: if (thisNode.compareDocumentPosition(otherNode) & Node.DOCUMENT_POSITION_FOLLOWING) {// otherNode is following thisNode}
*/
ElementPrototype.compareDocumentPosition = function(otherElement) {
// see http://ejohn.org/blog/comparing-document-position/
var instance = this,
parent, index1, index2, vChildNodes, vnode, otherVNode,
i_instance, i_other, sameLevel, arrayInstance, arrayOther;
if (instance===otherElement) {
return 0;
}
if (!DOCUMENT.contains(instance, null, true) || !DOCUMENT.contains(otherElement, null, true)) {
return 1;
}
else if (instance.contains(otherElement)) {
return 20;
}
else if (otherElement.contains(instance)) {
return 10;
}
parent = instance.getParent();
vChildNodes = parent.vnode.vChildNodes;
vnode = instance.vnode;
otherVNode = otherElement.vnode;
index1 = vChildNodes.indexOf(vnode);
index2 = vChildNodes.indexOf(otherVNode);
if ((index1!==-1) && (index2!==-1)) {
if (index1<index2) {
return 4;
}
else {
return 2;
}
}
// still not found, now we need to inspect the tree-structure of both elements
// and determine at what point (up-down the tree) the elements are going to differ
arrayInstance = [];
arrayOther = [];
arrayInstance[0] = vnode;
/*jshint boss:true */
while (vnode=vnode.vParent) {
/*jshint boss:false */
arrayInstance[arrayInstance.length] = vnode;
}
arrayOther[0] = otherVNode;
/*jshint boss:true */
while (otherVNode=otherVNode.vParent) {
/*jshint boss:false */
arrayOther[arrayOther.length] = otherVNode;
}
i_instance = arrayInstance.length - 1;
i_other = arrayOther.length - 1;
sameLevel = true;
while (sameLevel && (i_instance>=0) && (i_other>=0)) {
// starts with the most upper element
vnode = arrayInstance[i_instance];
otherVNode = arrayOther[i_other];
sameLevel = (vnode===otherVNode);
i_instance--;
i_other--;
}
// now we are out and we should be able to compare `vnode` and `otherVNode` which lie at the same level though are different
parent = vnode.vParent;
vChildNodes = parent.vChildNodes;
index1 = vChildNodes.indexOf(vnode);
index2 = vChildNodes.indexOf(otherVNode);
if (index1<index2) {
return 4;
}
else {
return 2;
}
};
ElementPrototype._contains = ElementPrototype.contains; // backup native _contains --> mutationobserver needs it
/**
* Indicating whether this Element contains OR equals otherElement. If you need only to be sure the other Element lies inside,
* but not equals itself, set `excludeItself` true.
*
* @method contains
* @param otherElement {Element}
* @param [excludeItself=false] {Boolean} to exclude itself as a hit
* @param [inspectProtectedNodes=false] {Boolean} no deepsearch in protected Nodes or iTags --> by default, these elements should be hidden
* @return {Boolean} whether this Element contains OR equals otherElement.
*/
ElementPrototype.contains = function(otherElement, excludeItself, inspectProtectedNodes) {
if (otherElement===this) {
return !excludeItself;
}
return !!otherElement && this.vnode.contains(otherElement.vnode, !inspectProtectedNodes);
};
/**
* Returns a newly created TreeWalker object with this Element as root.
*
* The TreeWalker is life presentation of the dom. It gets updated when the dom changes.
*
* @method createTreeWalker
* @param root {Element} The root node at which to begin the NodeIterator's traversal.
* @param [whatToShow] {Number} Filter specification constants from the NodeFilter DOM interface, indicating which nodes to iterate over.
* You can use or sum one of the next properties:
* <ul>
* <li>window.NodeFilter.SHOW_ALL === -1</li>
* <li>window.NodeFilter.SHOW_ELEMENT === 1</li>
* <li>window.NodeFilter.SHOW_COMMENT === 128</li>
* <li>window.NodeFilter.SHOW_TEXT === 4</li>
* </ul>
*
* A treewalker has the next methods:
* <ul>
* <li>treewalker.firstChild()</li>
* <li>treewalker.lastChild()</li>
* <li>treewalker.nextNode()</li>
* <li>treewalker.nextSibling()</li>
* <li>treewalker.parentNode()</li>
* <li>treewalker.previousNode()</li>
* <li>treewalker.previousSibling()</li>
* </ul>
*
* A treewalker has the next properties:
* <ul>
* <li>treewalker.currentNode</li>
* <li>treewalker.filter</li>
* <li>treewalker.root</li>
* <li>treewalker.whatToShow</li>
* </ul>
*
* @param [filter] {NodeFilter|function} An object implementing the NodeFilter interface or a function. See https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter
* @return {TreeWalker}
* @since 0.0.1
*/
ElementPrototype.createTreeWalker = function(whatToShow, filter) {
var treeWalker = Object.create(treeWalkerProto);
treeWalker._init(this, whatToShow, filter);
return treeWalker;
};
/**
* Sets the inline-style of the Element exactly to the specified `value`, overruling previous values.
* Making the Element's inline-style look like: style="value".
*
* This is meant for a quick one-time setup. For individually inline style-properties to be set, you can use `setInlineStyle()`.
*
* @method defineInlineStyle
* @param value {String} the style string to be set
* @chainable
* @since 0.0.1
*/
ElementPrototype.defineInlineStyle = function(value) {
return this.setAttr(STYLE, value);
};
/**
* Empties the content of the Element.
* Alias for thisNode.vTextContent = '';
*
* @method empty
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit
* @param [full=false] {Boolean} whether system-nodes should be removed as well
* @chainable
* @since 0.0.1
*/
ElementPrototype.empty = function(silent, full) {
var prevSuppress = DOCUMENT._suppressMutationEvents || false;
silent && DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
this.vnode.empty(full);
silent && DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(prevSuppress);
};
/**
* Reference to the first of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @method first
* @param [cssSelector] {String} to return the first Element that matches the css-selector
* @param [container] {HTMLElement} the container-element to search within --> this lead into searching out of the same level
* @return {Element}
* @since 0.0.1
*/
ElementPrototype.first = function(cssSelector, container) {
var parent, firstV;
if (container) {
return container.querySelector(cssSelector);
}
parent = this.vnode.vParent;
firstV = parent && parent.firstOfVChildren(cssSelector);
return firstV && firstV.domNode;
};
/**
* Reference to the first child-Element, where the related dom-node an Element (nodeType===1).
*
* @method firstOfChildren
* @param [cssSelector] {String} to return the first Element that matches the css-selector
* @return {Element}
* @since 0.0.1
*/
ElementPrototype.firstOfChildren = function(cssSelector) {
var foundVNode = this.vnode.firstOfVChildren(cssSelector);
return foundVNode && foundVNode.domNode;
};
/**
* Forces the Element to be inside an ancestor-Element that has the `overfow="scroll" set.
*
* @method forceIntoNodeView
* @param [ancestor] {Element} the Element where it should be forced into its view.
* Only use this when you know the ancestor and this ancestor has an `overflow="scroll"` property
* when not set, this method will seek through the doc-tree upwards for the first Element that does match this criteria.
* @chainable
* @since 0.0.1
*/
ElementPrototype.forceIntoNodeView = function(ancestor) {
// TODO: transitioned: http://wibblystuff.blogspot.nl/2014/04/in-page-smooth-scroll-using-css3.html
console.log(NAME, 'forceIntoNodeView');
var instance = this,
parentOverflowNode = this.getParent(),
match, left, width, right, height, top, bottom, scrollLeft, scrollTop, parentOverflowNodeX, parentOverflowNodeY,
parentOverflowNodeStartTop, parentOverflowNodeStartLeft, parentOverflowNodeStopRight, parentOverflowNodeStopBottom, newX, newY;
if (parentOverflowNode) {
if (ancestor) {
parentOverflowNode = ancestor;
}
else {
while (parentOverflowNode && (parentOverflowNode!==DOCUMENT) && !(match=((parentOverflowNode.getStyle(OVERFLOW)===SCROLL) || (parentOverflowNode.getStyle(OVERFLOW+'-y')===SCROLL)))) {
parentOverflowNode = parentOverflowNode.getParent();
}
}
if (parentOverflowNode && (parentOverflowNode!==DOCUMENT)) {
left = instance.left;
width = instance.offsetWidth;
right = left + width;
height = instance.offsetHeight;
top = instance.top;
bottom = top + height;
scrollLeft = parentOverflowNode.scrollLeft;
scrollTop = parentOverflowNode.scrollTop;
parentOverflowNodeX = parentOverflowNode.left;
parentOverflowNodeY = parentOverflowNode.top;
parentOverflowNodeStartTop = parentOverflowNodeY+parseInt(parentOverflowNode.getStyle(BORDER_TOP_WIDTH), 10);
parentOverflowNodeStartLeft = parentOverflowNodeX+parseInt(parentOverflowNode.getStyle(BORDER_LEFT_WIDTH), 10);
parentOverflowNodeStopRight = parentOverflowNodeX+parentOverflowNode.offsetWidth-parseInt(parentOverflowNode.getStyle(BORDER_RIGHT_WIDTH), 10);
parentOverflowNodeStopBottom = parentOverflowNodeY+parentOverflowNode.offsetHeight-parseInt(parentOverflowNode.getStyle(BORDER_BOTTOM_WIDTH), 10);
if (left<parentOverflowNodeStartLeft) {
newX = Math.max(0, scrollLeft+left-parentOverflowNodeStartLeft);
}
else if (right>parentOverflowNodeStopRight) {
newX = scrollLeft + right - parentOverflowNodeStopRight;
}
if (top<parentOverflowNodeStartTop) {
newY = Math.max(0, scrollTop+top-parentOverflowNodeStartTop);
}
else if (bottom>parentOverflowNodeStopBottom) {
newY = scrollTop + bottom - parentOverflowNodeStopBottom;
}
if ((newX!==undefined) || (newY!==undefined)) {
parentOverflowNode.scrollTo((newX!==undefined) ? newX : scrollLeft,(newY!==undefined) ? newY : scrollTop);
}
}
}
return instance;
};
/**
* Forces the Element to be inside the window-view. Differs from `scrollIntoView()` in a way
* that `forceIntoView()` doesn't change the position when it's inside the view, whereas
* `scrollIntoView()` sets it on top of the view.
*
* @method forceIntoView
* @param [notransition=false] {Boolean} set true if you are sure positioning is without transition.
* this isn't required, but it speeds up positioning. Only use when no transition is used:
* when there is a transition, setting this argument `true` would miscalculate the position.
* @param [rectangle] {Object} Set this if you have already calculated the window-rectangle (used for preformance within drag-drop)
* @param [rectangle.x] {Number} scrollLeft of window
* @param [rectangle.y] {Number} scrollTop of window
* @param [rectangle.w] {Number} width of window
* @param [rectangle.h] {Number} height of window
* @chainable
* @since 0.0.2
*/
ElementPrototype.forceIntoView = function(notransition, rectangle) {
// TODO: 'notransition' can be calculated with this.getTransition(left) this.getTransition(left)
// TODO: transitioned: http://wibblystuff.blogspot.nl/2014/04/in-page-smooth-scroll-using-css3.html
console.log(NAME, 'forceIntoView');
var instance = this,
left = instance.left,
width = instance.offsetWidth,
right = left + width,
height = instance.offsetHeight,
top = instance.top,
bottom = top + height,
windowLeft, windowTop, windowRight, windowBottom, newX, newY;
if (rectangle) {
windowLeft = rectangle.x;
windowTop = rectangle.y;
windowRight = rectangle.w;
windowBottom = rectangle.h;
}
else {
windowLeft = window.getScrollLeft();
windowTop = window.getScrollTop();
windowRight = windowLeft + window.getWidth();
windowBottom = windowTop + window.getHeight();
}
if (left<windowLeft) {
newX = Math.max(0, left);
}
else if (right>windowRight) {
newX = windowLeft + right - windowRight;
}
if (top<windowTop) {
newY = Math.max(0, top);
}
else if (bottom>windowBottom) {
newY = windowTop + bottom - windowBottom;
}
if ((newX!==undefined) || (newY!==undefined)) {
window.scrollTo((newX!==undefined) ? newX : windowLeft, (newY!==undefined) ? newY : windowTop);
}
return instance;
};
/**
* Gets an ElementArray of Elements that lie within this Element and match the css-selector.
*
* @method getAll
* @param cssSelector {String} css-selector to match
* @param [inspectProtectedNodes=false] {Boolean} no deepsearch in protected Nodes or iTags --> by default, these elements should be hidden
* @return {ElementArray} ElementArray of Elements that match the css-selector
* @since 0.0.1
*/
ElementPrototype.getAll = function(cssSelector, inspectProtectedNodes) {
return this.querySelectorAll(cssSelector, inspectProtectedNodes);
};
/**
* Gets an attribute of the Element.
*
* Alias for getAttribute().
*
* @method getAttr
* @param attributeName {String}
* @return {String|null} value of the attribute
* @since 0.0.1
*/
ElementPrototype.getAttr = function(attributeName) {
return this.vnode.attrs[attributeName] || null;
};
/**
* Returns all attributes as defined as an key/value object.
*
* @method getAttrs
* @param attributeName {String}
* @return {Object} all attributes as on Object
* @since 0.0.1
*/
ElementPrototype.getAttrs = function() {
return this.vnode.attrs;
};
/**
* Gets an attribute of the Element.
*
* Same as getAttr().
*
* @method getAttribute
* @param attributeName {String}
* @return {String|null} value of the attribute
* @since 0.0.1
*/
ElementPrototype._getAttribute = ElementPrototype.getAttribute;
ElementPrototype.getAttribute = function(attributeName) {
return this.vnode.attrs[attributeName] || null;
};
/**
* Returns a live collection of the Element-childNodes.
*
* @method getChildren
* @return {ElementArray}
* @since 0.0.1
*/
ElementPrototype.getChildren = function() {
var vChildren = this.vnode.vChildren,
len = vChildren.length,
children = ElementArray.createArray(),
i;
for (i=0; i<len; i++) {
children[children.length] = vChildren[i].domNode;
}
return children;
};
/**
* Returns a token list of the class attribute of the element.
* See: https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList
*
* @method getClassList
* @return DOMTokenList
* @since 0.0.1
*/
ElementPrototype.getClassList = function() {
var instance = this,
vnode = instance.vnode;
if (!vnode._classList) {
vnode._classList = Object.create(classListProto);
vnode._classList._init(instance);
}
return vnode._classList;
};
/**
* Returns data set specified by `key`. If not set, `undefined` will be returned.
* The data is efficiently stored on the vnode.
*
* @method getData
* @param key {string} name of the key
* @return {Any|undefined} data set specified by `key`
* @since 0.0.1
*/
ElementPrototype.getData = function(key) {
var vnode = this.vnode;
return vnode._data && vnode._data[key];
};
/**
* Gets one Element, specified by the css-selector. To retrieve a single element by id,
* you need to prepend the id-name with a `#`. When multiple Element's match, the first is returned.
*
* @method getElement
* @param cssSelector {String} css-selector to match
* @param [inspectProtectedNodes=false] {Boolean} no deepsearch in protected Nodes or iTags --> by default, these elements should be hidden
* @return {Element|null} the Element that was search for
* @since 0.0.1
*/
ElementPrototype.getElement = function(cssSelector, inspectProtectedNodes) {
return ((cssSelector[0]==='#') && (cssSelector.indexOf(' ')===-1)) ? this.getElementById(cssSelector.substr(1), inspectProtectedNodes) : this.querySelector(cssSelector, inspectProtectedNodes);
};
/**
* Returns the Element matching the specified id, which should should be a descendant of this Element.
*
* @method getElementById
* @param id {String} id of the Element
* @param [inspectProtectedNodes=false] {Boolean} no deepsearch in protected Nodes or iTags --> by default, these elements should be hidden
* @return {Element|null}
*
*/
ElementPrototype.getElementById = function(id, inspectProtectedNodes) {
var element = nodeids[id];
if (element && !this.contains(element, true, inspectProtectedNodes)) {
// outside itself
return null;
}
return element || null;
};
/**
* Gets innerHTML of the dom-node.
* Goes through the vdom, so it's superfast.
*
* Use this method instead of `innerHTML`
*
* @method getHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String}
* @since 0.0.1
*/
ElementPrototype.getHTML = function(exclude, includeSystemNodes) {
return this.vnode.getHTML(exclude, includeSystemNodes);
};
/**
* Returns the Elments `id`
*
* @method getId
* @return {String|undefined} Elements `id`
* @since 0.0.1
*/
ElementPrototype.getId = function() {
return this.vnode.id;
};
/**
* Returns inline style of the specified property. `Inline` means: what is set directly on the Element,
* this doesn't mean necesairy how it is looked like: when no css is set inline, the Element might still have
* an appearance because of other CSS-rules.
*
* In most cases, you would be interesting in using `getStyle()` instead.
*
* Note: no need to camelCase cssProperty: both `margin-left` as well as `marginLeft` are fine
*
* @method getInlineStyle
* @param cssProperty {String} the css-property to look for
* @param [pseudo] {String} to look inside a pseudo-style
* @return {String|undefined} css-style
* @since 0.0.1
*/
ElementPrototype.getInlineStyle = function(cssProperty, pseudo) {
var styles = this.vnode.styles,
groupStyle = styles && styles[pseudo || 'element'],
value;
if (groupStyle) {
value = groupStyle[fromCamelCase(cssProperty)];
value && (cssProperty===VENDOR_TRANSITION_PROPERTY) && (value=extractor.serializeTransition(value));
}
return value;
};
/**
* Returns inline transition-css-property. `Inline` means: what is set directly on the Element,
* When `transition` is set inline, no `parent` transition-rules apply.
*
*
* @method getInlineTransition
* @param [transitionProperty] {String} the css-property to look for
* @param [pseudo] {String} to look inside a pseudo-style
* @return {Object} the transition-object, with the properties:
* <ul>
* <li>duration {Number}</li>
* <li>timingFunction {String}</li>
* <li>delay {Number}</li>
* </ul>
* @since 0.0.1
*/
ElementPrototype.getInlineTransition = function(transitionProperty, pseudo) {
var styles = this.vnode.styles,
groupStyle = styles && styles[pseudo || 'element'],
transitionStyles = groupStyle && groupStyle[VENDOR_TRANSITION_PROPERTY];
if (transitionStyles) {
return transitionProperty ? transitionStyles[fromCamelCase(transitionProperty)] : transitionStyles;
}
};
/**
* Gets the outerHTML of the dom-node.
* Goes through the vdom, so it's superfast.
*
* Use this method instead of `outerHTML`
*
* @method getOuterHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String}
* @since 0.0.1
*/
ElementPrototype.getOuterHTML = function(exclude, includeSystemNodes) {
return this.vnode.getOuterHTML(exclude, includeSystemNodes);
};
/**
* Returns the Element's parent Element.
*
* @method getParent
* @return {Element}
*/
ElementPrototype.getParent = function() {
var vParent = this.vnode.vParent;
return vParent && vParent.domNode;
};
/**
* Returns cascaded style of the specified property. `Cascaded` means: the actual present style,
* the way it is visible (calculated through the DOM-tree).
*
* <ul>
* <li>Note1: values are absolute: percentages and points are converted to absolute values, sizes are in pixels, colors in rgb/rgba-format.</li>
* <li>Note2: you cannot query shotcut-properties: use `margin-left` instead of `margin`.</li>
* <li>Note3: no need to camelCase cssProperty: both `margin-left` as well as `marginLeft` are fine.</li>
* <li>Note4: you can query `transition`, `transform`, `perspective` and `transform-origin` instead of their vendor-specific properties.</li>
* <li>Note5: `transition` or `transform` return an Object instead of a String.</li>
* </ul>
*
* @method getCascadeStyle
* @param cssProperty {String} property that is queried
* @param [pseudo] {String} to query pseudo-element, fe: `:before` or `:first-line`
* @return {String|Object} value for the css-property: this is an Object for the properties `transition` or `transform`
* @since 0.0.1
*/
ElementPrototype.getStyle = function(cssProperty, pseudo) {
// Cautious: when reading the property `transform`, getComputedStyle should
// read the calculated value, but some browsers (webkit) only calculate the style on the current element
// In those cases, we need a patch and look up the tree ourselves
// Also: we will return separate value, NOT matrices
var instance = this;
if (cssProperty===VENDOR_TRANSITION_PROPERTY) {
return instance._getTransitionAll(pseudo);
}
VENDOR_CSS_PROPERTIES[cssProperty] || (cssProperty=generateVendorCSSProp(cssProperty));
return window.getComputedStyle(instance, pseudo)[toCamelCase(cssProperty)];
};
/**
* Returns cascaded "transition" style of the specified trandform-property. `Cascaded` means: the actual present style,
* the way it is visible (calculated through the DOM-tree).
*
* Note1: When "transition" is set inline, ONLY inline transtition is active!
* Thus, if parentNode has "transition: width 2s" and inline has "transition: height 3s", then the transition
* will be "transition: height 3s" --> returning "undefined" for transitionProperty=width.
* Note2: in case of "transition: all" --> these values will be returned for every "transitionProperty" (even when querying "width")
*
* @method getTransition
* @param transitionProperty {String} transform property that is queried, f.e. "width", or "all"
* @param [pseudo] {String} to query pseudo-element, fe: `:before` or `:first-line`
* @return {Object} the transition-object, with the properties:
* <ul>
* <li>duration {Number}</li>
* <li>timingFunction {String}</li>
* <li>delay {Number}</li>
* </ul>
* @since 0.0.1
*/
ElementPrototype.getTransition = function(transitionProperty, pseudo) {
var instance = this,
transProperty, transDuration, transTimingFunction, transDelay, transPropertySplitted,
transition, transDurationSplitted, transTimingFunctionSplitted, transDelaySplitted, index;
if (instance.hasInlineStyle(VENDOR_TRANSITION_PROPERTY, pseudo)) {
transition = instance.getInlineTransition(transitionProperty, pseudo);
// if not found, then search for "all":
transition || (transition=instance.getInlineTransition('all', pseudo));
if (transition) {
// getTransition always returns all the properties:
transition.timingFunction || (transition.timingFunction='ease');
transition.delay || (transition.delay=0);
}
return transition;
}
transProperty = instance.getStyle(VENDOR_TRANSITION_PROPERTY+'Property', pseudo);
transDuration = instance.getStyle(VENDOR_TRANSITION_PROPERTY+'Duration', pseudo);
transTimingFunction = instance.getStyle(VENDOR_TRANSITION_PROPERTY+'TimingFunction', pseudo);
transDelay = instance.getStyle(VENDOR_TRANSITION_PROPERTY+'Delay', pseudo);
transPropertySplitted = transProperty && transProperty.split(',');
if (transProperty) {
if (transPropertySplitted.length>1) {
// multiple definitions
index = transPropertySplitted.indexOf(transitionProperty);
// the array is in a form like this: 'width, height, opacity' --> therefore, we might need to look at a whitespace
if (index===-1) {
index = transPropertySplitted.indexOf(' '+transitionProperty);
// if not found, then search for "all":
if (index===-1) {
index = transPropertySplitted.indexOf('all');
(index===-1) && (index=transPropertySplitted.indexOf(' '+'all'));
}
}
if (index!==-1) {
transDurationSplitted = transDuration.split(',');
transTimingFunctionSplitted = transTimingFunction.split(',');
transDelaySplitted = transDelay.split(',');
transition = {
duration: parseFloat(transDurationSplitted[index]),
timingFunction: transTimingFunctionSplitted[index].trimLeft(),
delay: parseFloat(transDelaySplitted)
};
}
}
else {
// one definition
if ((transProperty===transitionProperty) || (transProperty==='all')) {
transition = {
duration: parseFloat(transDuration),
timingFunction: transTimingFunction,
delay: parseFloat(transDelay)
};
}
}
transition && (transition.duration===0) && (transition=undefined);
return transition;
}
};
/**
* Elements tag-name in uppercase (same as nodeName).
*
* @method getTagName
* @return {String}
* @since 0.0.1
*/
ElementPrototype.getTagName = function() {
return this.vnode.tag;
};
/**
* Gets the innerContent of the Element as plain text.
* Goes through the vdom, so it's superfast.
*
* Use this method instead of `textContent`
*
* @method getText
* @return String
* @since 0.0.1
*/
ElementPrototype.getText = function() {
return this.vnode.textContent;
};
/**
* Gets the value of the following Elements:
*
* <ul>
* <li>input</li>
* <li>textarea</li>
* <li>select</li>
* <li>any container that is `contenteditable`</li>
* </ul>
*
* @method getValue
* @return {String}
* @since 0.0.1
*/
ElementPrototype.getValue = function() {
// cautious: input and textarea must be accessed by their propertyname:
// input.getAttribute('value') would return the default-value instead of actual
// and textarea.getAttribute('value') doesn't exist
var instance = this,
contenteditable = instance.vnode.attrs.contenteditable,
editable = contenteditable && (contenteditable!=='false');
return editable ? instance.getHTML() : instance.value;
};
/**
* Whether the Element has the attribute set.
*
* Alias for hasAttribute().
*
* @method hasAttr
* @param attributeName {String}
* @return {Boolean} Whether the Element has the attribute set.
* @since 0.0.1
*/
ElementPrototype.hasAttr = function(attributeName) {
return !!this.vnode.attrs[attributeName];
};
/**
* Whether the Element has the attribute set.
*
* Same as hasAttr().
*
* @method hasAttribute
* @param attributeName {String}
* @return {Boolean} Whether the Element has the attribute set.
* @since 0.0.1
*/
ElementPrototype.hasAttribute = function(attributeName) {
return !!this.vnode.attrs[attributeName];
};
/**
* Indicating if the current element has any attributes or not.
*
* @method hasAttributes
* @return {Boolean} Whether the current element has any attributes or not.
*/
ElementPrototype.hasAttributes = function() {
var attrs = this.vnode.attrs;
return attrs ? (attrs.size() > 0) : false;
};
/**
* Indicating if the Element has any children (childNodes with nodeType of 1).
*
* @method hasChildren
* @return {Boolean} whether the Element has children
* @since 0.0.1
*/
ElementPrototype.hasChildren = function() {
return this.vnode.hasVChildren();
};
/**
* Checks whether the className is present on the Element.
*
* @method hasClass
* @param className {String|Array} the className to check for. May be an Array of classNames, which all needs to be present.
* @return {Boolean} whether the className (or classNames) is present on the Element
* @since 0.0.1
*/
ElementPrototype.hasClass = function(className) {
return this.getClassList().contains(className);
};
/**
* If the Element has data set specified by `key`. The data could be set with `setData()`.
*
* @method hasData
* @param key {string} name of the key
* @return {Boolean}
* @since 0.0.1
*/
ElementPrototype.hasData = function(key) {
var vnode = this.vnode;
return !!(vnode._data && (vnode._data[key]!==undefined));
};
/**
* Indicates whether Element currently has the focus.
*
* @method hasFocus
* @return {Boolean}
* @since 0.0.1
*/
ElementPrototype.hasFocus = function() {
return (DOCUMENT.activeElement===this);
};
/**
* Indicates whether the current focussed Element lies inside this Element (on a descendant Element).
*
* @method hasFocusInside
* @return {Boolean}
* @since 0.0.1
*/
ElementPrototype.hasFocusInside = function() {
return this.contains(DOCUMENT.activeElement, true);
};
/**
* Returns whether the inline style of the specified property is present. `Inline` means: what is set directly on the Element.
*
* Note: no need to camelCase cssProperty: both `margin-left` as well as `marginLeft` are fine
*
* @method hasInlineStyle
* @param cssProperty {String} the css-property to look for
* @param [pseudo] {String} to look inside a pseudo-style
* @return {Boolean} whether the inlinestyle was present
* @since 0.0.1
*/
ElementPrototype.hasInlineStyle = function(cssProperty, pseudo) {
return !!this.getInlineStyle(cssProperty, pseudo);
};
/**
* Returns whether the specified inline transform-css-property is present. `Inline` means: what is set directly on the Element.
*
* See more about tranform-properties: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
*
* @method hasInlineTransition
* @param transitionProperty {String} the css-property to look for
* @param [pseudo] {String} to look inside a pseudo-style
* @return {Boolean} whether the inline transform-css-property was present
* @since 0.0.1
*/
ElementPrototype.hasInlineTransition = function(transitionProperty, pseudo) {
return !!this.getInlineTransition(transitionProperty, pseudo);
};
/**
* Returns whether the specified transform-property is active.
*
* Note1: When "transition" is set inline, ONLY inline transtition is active!
* Thus, if parentNode has "transition: width 2s" and inline has "transition: height 3s",
* then hasTransition('width') will return false.
* Note2: in case of "transition: all" --> hasTransition() will always `true` for every transitionProperty.
*
* @method hasTransition
* @param transitionProperty {String} the css-property to look for
* @param [pseudo] {String} to look inside a pseudo-style
* @return {Boolean} whether the inlinestyle was present
* @since 0.0.1
*/
ElementPrototype.hasTransition = function(transitionProperty, pseudo) {
return !!this.getTransition(transitionProperty, pseudo);
};
/**
* Hides a node by making it floated and removing it out of the visible screen.
* Hides immediately without `fade`, or will fade when fade is specified.
*
* @method hide
* @param [fade] {Number} sec to fade (you may use `0.1`)
* @return {this|Promise} fulfilled when the element is ready hiding, or rejected when showed up again (using node.show) before fully hided.
* @since 0.0.1
*/
ElementPrototype.hide = function(duration) {
// when it doesn't have, it doesn;t harm to leave the transitionclass on: it would work anyway
// nevertheless we will remove it with a timeout
var instance = this,
showPromise = instance.getData('_showNodeBusy'),
hidePromise = instance.getData('_hideNodeBusy'),
originalOpacity, hasOriginalOpacity, promise, freezedOpacity, fromOpacity;
instance.setData('nodeShowed', false); // for any routine who wants to know
originalOpacity = instance.getData('_showNodeOpacity');
if (!originalOpacity && !showPromise && !hidePromise) {
originalOpacity = parseFloat(instance.getInlineStyle('opacity'));
instance.setData('_showNodeOpacity', originalOpacity);
}
hasOriginalOpacity = !!originalOpacity;
showPromise && showPromise.freeze();
if (showPromise) {
showPromise.freeze();
instance.removeData('_showNodeBusy');
}
hidePromise && hidePromise.freeze();
if (duration) {
if (showPromise || hidePromise) {
freezedOpacity = instance.getInlineStyle('opacity');
fromOpacity = originalOpacity || 1;
duration = (fromOpacity>0) ? Math.min(1, (freezedOpacity/fromOpacity))*duration : 0;
}
promise = instance.transition({property: 'opacity', value: 0, duration: duration});
instance.setData('_hideNodeBusy', promise);
promise.finally(
function() {
if (!promise.cancelled && !promise.frozen) {
instance.setClass(HIDDEN);
originalOpacity ? instance.setInlineStyle('opacity', originalOpacity) : instance.removeInlineStyle('opacity');
}
instance.removeData('_hideNodeBusy');
}
);
return promise;
}
else {
async(function() {
instance.setClass(HIDDEN);
hasOriginalOpacity ? instance.setInlineStyle('opacity', originalOpacity) : instance.removeInlineStyle('opacity');
});
return instance;
}
};
/**
* Indicates whether the Element currently is part if the DOM.
*
* @method inDOM
* @return {Boolean} whether the Element currently is part if the DOM.
* @since 0.0.1
*/
ElementPrototype.inDOM = function() {
if (this.vnode.removedFromDOM) {
return false;
}
return DOCUMENT.contains(this, false, true);
};
/**
* Checks whether the Element lies within the specified selector (which can be a CSS-selector or a Element)
*
* @example
* var divnode = childnode.inside('div.red');
*
* @example
* var divnode = childnode.inside(containerNode);
*
* @method inside
* @param selector {Element|String} the selector, specified by a Element or a css-selector
* @return {Element|false} the nearest Element that matches the selector, or `false` when not found
* @since 0.0.1
*/
ElementPrototype.inside = function(selector) {
var instance = this,
vParent;
if (typeof selector===STRING) {
vParent = instance.vnode.vParent;
while (vParent && !vParent.matchesSelector(selector)) {
vParent = vParent.vParent;
}
return vParent ? vParent.domNode : false;
}
else {
// selector should be an Element
return ((selector!==instance) && selector.contains(instance)) ? selector : false;
}
};
/**
* Checks whether a point specified with x,y is within the Element's region.
*
* @method insidePos
* @param x {Number} x-value for new position (coordinates are page-based)
* @param y {Number} y-value for new position (coordinates are page-based)
* @return {Boolean} whether there is a match
* @since 0.0.1
*/
ElementPrototype.insidePos = function(x, y) {
var instance = this,
left = instance.left,
top = instance.top,
right = left + instance.offsetWidth,
bottom = top + instance.offsetHeight;
return (x>=left) && (x<=right) && (y>=top) && (y<=bottom);
};
/**
* Inserts `domNode` before `refDomNode`.
*
* @method insertBefore
* @param domNode {Node|Element|ElementArray|String} content to insert
* @param refDomNode {Element} The Element before which newElement is inserted.
* @param [escape] {Boolean} whether to insert `escaped` content, leading it into only text inserted
* @return {Node} the Element being inserted (equals domNode)
*/
ElementPrototype._insertBefore = ElementPrototype.insertBefore;
ElementPrototype.insertBefore = function(domNode, refDomNode, escape) {
return this.prepend(domNode, escape, refDomNode);
};
/**
* Whether the element is an Itag-element
*
* @method isItag
* @return {Boolean}
* @since 0.0.1
*/
ElementPrototype.isItag = function() {
return this.vnode.isItag;
};
/**
* Reference to the last of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @method last
* @param [cssSelector] {String} to return the last Element that matches the css-selector
* @param [container] {HTMLElement} the container-element to search within --> this lead into searching out of the same level
* @return {Element}
* @since 0.0.1
*/
ElementPrototype.last = function(cssSelector, container) {
var vParent, lastV, found;
if (container) {
found = container.querySelectorAll(cssSelector);
return found[found.length-1];
}
vParent = this.vnode.vParent;
lastV = vParent && vParent.lastOfVChildren(cssSelector);
return lastV && lastV.domNode;
};
/**
* Reference to the last child-Element, where the related dom-node an Element (nodeType===1).
*
* @method lastOfChildren
* @param [cssSelector] {String} to return the last Element that matches the css-selector
* @return {Element}
* @since 0.0.1
*/
ElementPrototype.lastOfChildren = function(cssSelector) {
var foundVNode = this.vnode.lastOfVChildren(cssSelector);
return foundVNode && foundVNode.domNode;
};
/**
* Indicates if the element would be selected by the specified selector string.
* Alias for matchesSelector()
*
* @method matches
* @param [cssSelector] {String} the css-selector to check for
* @return {Boolean}
* @since 0.0.1
*/
ElementPrototype.matches = function(selectors) {
return this.vnode.matchesSelector(selectors);
};
/**
* Indicates if the element would be selected by the specified selector string.
* Alias for matches()
*
* @method matchesSelector
* @param [cssSelector] {String} the css-selector to check for
* @return {Boolean}
* @since 0.0.1
*/
ElementPrototype.matchesSelector = function(selectors) {
return this.vnode.matchesSelector(selectors);
};
/**
* Reference to the next of sibbling Element, where the related dom-node is an Element(nodeType===1).
*
* @method next
* @param [cssSelector] {String} css-selector to be used as a filter
* @param [container] {HTMLElement} the container-element to search within --> this lead into searching out of the same level
* @return {Element|null}
* @type Element
* @since 0.0.1
*/
ElementPrototype.next = function(cssSelector, container) {
var vnode = this.vnode,
found, vNextElement, firstCharacter, i, len;
if (container) {
return container.querySelector(cssSelector, false, this, 2) || null;
}
if (!cssSelector) {
vNextElement = vnode.vNextElement;
return vNextElement && vNextElement.domNode;
}
else {
i = -1;
len = cssSelector.length;
while (!firstCharacter && (++i<len)) {
firstCharacter = cssSelector[i];
(firstCharacter===' ') && (firstCharacter=null);
}
if (firstCharacter==='>') {
return null;
}
}
vNextElement = vnode;
do {
vNextElement = vNextElement.vNextElement;
found = vNextElement && vNextElement.matchesSelector(cssSelector);
} while(vNextElement && !found);
return found ? vNextElement.domNode : null;
};
/**
* Prepends a Element or text at the start of Element's innerHTML, or before the `refElement`.
*
* @method prepend
* @param content {Element|Element|ElementArray|String} content to prepend
* @param [escape] {Boolean} whether to insert `escaped` content, leading it into only text inserted
* @param [refElement] {Element} reference Element where the content should be prepended
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit
* @param [allowScripts=false] {Boolean} whether scripts are allowed --> these should be defined with `xscript` instead of `script`
* @return {Element} the created Element (or the last when multiple)
* @since 0.0.1
*/
ElementPrototype.prepend = function(content, escape, refElement, silent, allowScripts) {
var instance = this,
vnode = instance.vnode,
prevSuppress = DOCUMENT._suppressMutationEvents || false,
i, len, item, createdElement, vnodes, vChildNodes, _scripts, scriptcontent,
doPrepend = function(oneItem) {
escape && (oneItem.nodeType===1) && (oneItem=DOCUMENT.createTextNode(oneItem.getOuterHTML()));
createdElement = refElement ? vnode._insertBefore(oneItem.vnode, refElement.vnode) : vnode._appendChild(oneItem.vnode);
// CAUTIOUS: when using TextNodes, they might get merged (vnode._normalize does this), which leads into disappearance of refElement:
refElement = createdElement;
};
silent && DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
vnode._noSync()._normalizable(false);
if (!refElement) {
vChildNodes = vnode.vChildNodes;
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; (i<len) && !refElement; i++) {
vChildNodes[i]._systemNode || (refElement=vChildNodes[i].domNode);
}
}
}
(typeof content===STRING) && (content=htmlToVFragments(content, vnode.ns, allowScripts));
if (content.isFragment) {
vnodes = content.vnodes;
len = vnodes.length;
// to manage TextNodes which might get merged, we loop downwards:
for (i=len-1; i>=0; i--) {
doPrepend(vnodes[i].domNode);
}
/*jshint boss:true */
if (_scripts=content._scripts) {
/*jshint boss:false */
len = _scripts.length;
vnode._scripts || (vnode._scripts=[]);
for (i=0; i<len; i++) {
scriptcontent = _scripts[i];
if (!vnode._scripts.contains(scriptcontent)) {
vnode._scripts[vnode._scripts.length] = scriptcontent;
}
}
}
// in case a style-tag was added, we need to cleanup double definitions:
content._cleanupStyle && vnode._cleanupStyle();
}
else if (Array.isArray(content)) {
len = content.length;
// to manage TextNodes which might get merged, we loop downwards:
for (i=len-1; i>=0; i--) {
item = content[i];
doPrepend(item);
}
}
else {
doPrepend(content);
}
vnode._normalizable(true)._normalize();
silent && DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(prevSuppress);
return createdElement;
};
/**
* Reference to the previous of sibbling Element, where the related dom-node is an Element(nodeType===1).
*
* @method previous
* @param [cssSelector] {String} css-selector to be used as a filter
* @param [container] {HTMLElement} the container-element to search within --> this lead into searching out of the same level
* @return {Element|null}
* @type Element
* @since 0.0.1
*/
ElementPrototype.previous = function(cssSelector, container) {
var vnode = this.vnode,
found, vPreviousElement, firstCharacter, i, len;
if (container) {
found = container.querySelectorAll(cssSelector, false, this, 4);
return (found.length>0) ? found[found.length-1] : null;
}
if (!cssSelector) {
vPreviousElement = vnode.vPreviousElement;
return vPreviousElement && vPreviousElement.domNode;
}
else {
i = -1;
len = cssSelector.length;
while (!firstCharacter && (++i<len)) {
firstCharacter = cssSelector[i];
(firstCharacter===' ') && (firstCharacter=null);
}
if (firstCharacter==='>') {
return null;
}
}
vPreviousElement = vnode;
do {
vPreviousElement = vPreviousElement.vPreviousElement;
found = vPreviousElement && vPreviousElement.matchesSelector(cssSelector);
} while(vPreviousElement && !found);
return found ? vPreviousElement.domNode : null;
};
/**
* Returns the first Element within the Element, that matches the CSS-selectors. You can pass one, or multiple CSS-selectors. When passed multiple,
* they need to be separated by a `comma`.
*
* @method querySelector
* @param selectors {String} CSS-selector(s) that should match
* @param [inspectProtectedNodes=false] {Boolean} no deepsearch in protected Nodes or iTags --> by default, these elements should be hidden
* @param [refNode] {HTMLElement} reference-node where the found node should be `before` or `after`: specified by domPosition
* @param [domPosition] {Number} The position to accept, compared to `refNode`. Should be either:
* <ul>
* <li>Node.DOCUMENT_POSITION_PRECEDING === 2 (this Element comes before otherElement)</li>
* <li>Node.DOCUMENT_POSITION_FOLLOWING === 4 (this Element comes after otherElement)</li>
* </ul>
* @return {Element}
*/
ElementPrototype.querySelector = function(selectors, inspectProtectedNodes, refNode, domPosition) {
var found,
i = -1,
thisvnode = this.vnode,
len, firstCharacter, startvnode, inspectChildren;
selectors || (selectors='*');
len = selectors.length;
inspectChildren = function(vnode) {
var vChildren = vnode.vChildren,
len2 = vChildren ? vChildren.length : 0,
j, vChildNode, noDeep;
for (j=0; (j<len2) && !found; j++) {
vChildNode = vChildren[j];
if (vChildNode.matchesSelector(selectors, thisvnode) && (!refNode || ((vChildNode.domNode.compareDocumentPosition(refNode) & domPosition)!==0))) {
if (!vChildNode._systemNode || inspectProtectedNodes) {
found = vChildNode.domNode;
}
}
if (!found) {
if (!inspectProtectedNodes) {
if (vChildNode._systemNode || (vChildNode.isItag && vChildNode.domNode.contentHidden)) {
noDeep = true;
}
else {
noDeep = false;
}
}
else {
noDeep = false;
}
noDeep || inspectChildren(vChildNode);
}
}
};
while (!firstCharacter && (++i<len)) {
firstCharacter = selectors[i];
(firstCharacter===' ') && (firstCharacter=null);
}
startvnode = SIBLING_MATCH_CHARACTER[firstCharacter] ? thisvnode.vParent : thisvnode;
startvnode && inspectChildren(startvnode);
return found;
};
/**
* Returns an ElementArray of all Elements within the Element, that match the CSS-selectors. You can pass one, or multiple CSS-selectors. When passed multiple,
* they need to be separated by a `comma`.
*
* querySelectorAll is a snapshot of the dom at the time this method was called. It is not updated when changes of the dom are made afterwards.
*
* @method querySelectorAll
* @param selectors {String} CSS-selector(s) that should match
* @param [inspectProtectedNodes=false] {Boolean} no deepsearch in protected Nodes or iTags --> by default, these elements should be hidden
* @param [refNode] {HTMLElement} reference-node where the found nodes should be `before` or `after`: specified by domPosition
* @param [domPosition] {Number} The position to accept, compared to `refNode`. Should be either:
* <ul>
* <li>Node.DOCUMENT_POSITION_PRECEDING === 2 (this Element comes before otherElement)</li>
* <li>Node.DOCUMENT_POSITION_FOLLOWING === 4 (this Element comes after otherElement)</li>
* </ul>
* @return {ElementArray} non-life Array (snapshot) with Elements
*/
ElementPrototype.querySelectorAll = function(selectors, inspectProtectedNodes, refNode, domPosition) {
var found = ElementArray.createArray(),
i = -1,
thisvnode = this.vnode,
len, firstCharacter, startvnode, inspectChildren;
selectors || (selectors='*');
len = selectors.length,
inspectChildren = function(vnode) {
var vChildren = vnode.vChildren,
len2 = vChildren ? vChildren.length : 0,
j, vChildNode, noDeep;
for (j=0; j<len2; j++) {
vChildNode = vChildren[j];
if (vChildNode.matchesSelector(selectors, thisvnode) && (!refNode || ((vChildNode.domNode.compareDocumentPosition(refNode) & domPosition)!==0))) {
if (!vChildNode._systemNode || inspectProtectedNodes) {
found[found.length] = vChildNode.domNode;
}
}
if (!inspectProtectedNodes) {
if (vChildNode._systemNode || (vChildNode.isItag && vChildNode.domNode.contentHidden)) {
noDeep = true;
}
else {
noDeep = false;
}
}
else {
noDeep = false;
}
noDeep || inspectChildren(vChildNode);
}
};
while (!firstCharacter && (++i<len)) {
firstCharacter = selectors[i];
(firstCharacter===' ') && (firstCharacter=null);
}
startvnode = SIBLING_MATCH_CHARACTER[firstCharacter] ? thisvnode.vParent : thisvnode;
startvnode && inspectChildren(startvnode);
return found;
};
/**
* Checks whether the Element has its rectangle inside the outbound-Element.
* This is no check of the DOM-tree, but purely based upon coordinates.
*
* @method rectangleInside
* @param outboundElement {Element} the Element where this element should lie inside
* @return {Boolean} whether the Element lies inside the outboundElement
* @since 0.0.1
*/
ElementPrototype.rectangleInside = function(outboundElement) {
var instance = this,
outerRect = outboundElement.getBoundingClientRect(),
innerRect = instance.getBoundingClientRect();
return (outerRect.left<=innerRect.left) &&
(outerRect.top<=innerRect.top) &&
((outerRect.left+outboundElement.offsetWidth)>=(innerRect.left+instance.offsetWidth)) &&
((outerRect.top+outboundElement.offsetHeight)>=(innerRect.top+instance.offsetHeight));
};
/**
* Removes the Element from the DOM.
* Alias for thisNode.parentNode.removeChild(thisNode);
*
* @method remove
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit
* @return {Node} the DOM-node that was removed. You could re-insert it at a later time.
* @since 0.0.1
*/
ElementPrototype.remove = function(silent) {
var instance = this,
vnode = instance.vnode,
prevSuppress = DOCUMENT._suppressMutationEvents || false,
vParent = vnode.vParent;
silent && DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
vParent && vParent._removeChild(vnode);
silent && DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(prevSuppress);
return instance;
};
/**
* Removes the attribute from the Element.
*
* Alias for removeAttribute() BUT is chainable instead (removeAttribute is not).
*
* @method removeAttr
* @param attributeName {String}
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit
* @chainable
* @since 0.0.1
*/
ElementPrototype.removeAttr = function(attributeName, silent) {
this.removeAttribute(attributeName, silent);
return this;
};
/**
* Removes multiple attributes on the Element.
* The argument should be one ore more AttributeNames.
*
* @example
* instance.removeAttrs(['tabIndex', 'style']);
*
* @method removeAttrs
* @param attributeData {Array|String}
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit
* @chainable
* @since 0.0.1
*/
ElementPrototype.removeAttrs = function(attributeData, silent) {
var instance = this;
Array.isArray(attributeData) || (attributeData=[attributeData]);
attributeData.forEach(function(item) {
instance.removeAttribute(item, silent);
});
return instance;
};
/**
* Removes the attribute from the Element.
*
* Use removeAttr() to be able to chain.
*
* @method removeAttr
* @param attributeName {String}
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit
* @since 0.0.1
*/
ElementPrototype._removeAttribute = ElementPrototype.removeAttribute;
ElementPrototype.removeAttribute = function(attributeName, silent) {
var prevSuppress = DOCUMENT._suppressMutationEvents || false;
silent && DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
this.vnode._removeAttr(attributeName);
silent && DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(prevSuppress);
};
/**
* Removes the attribute of the Elementinside a specified namespace
*
* @method removeAttributeNS
* @param nameSpace {String} the namespace where to attribuyte should be set in
* @param attributeName {String}
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit
*/
ElementPrototype._removeAttributeNS = ElementPrototype.removeAttributeNS;
ElementPrototype.removeAttributeNS = function(nameSpace, attributeName, silent) {
this.removeAttribute((nameSpace ? nameSpace+':' : '')+attributeName, silent);
};
/**
* Removes the Element's child-Node from the DOM.
*
* @method removeChild
* @param domNode {Node} the child-Node to remove
* @return {Node} the DOM-node that was removed. You could re-insert it at a later time.
*/
ElementPrototype._removeChild = ElementPrototype.removeChild;
ElementPrototype.removeChild = function(domNode) {
var instance = this;
instance.vnode._removeChild(domNode.vnode);
return instance;
};
/**
* Removes a className from the Element.
*
* @method removeClass
* @param className {String|Array} the className that should be removed. May be an Array of classNames.
* @param [returnPromise] {Boolean} whether to return a Promise instead of `this`, which might be useful in case of
* transition-properties. The promise will fullfil when the transition is ready, or immediately when no transitioned.
* @param [transitionFix] set this to `true` if you experience transition-problems due to wrong calculated css (mostly because of the `auto` value)
* Setting this parameter, will calculate the true css of the transitioned properties and set this temporarely inline, to fix the issue.
* Don't use it when not needed, it has a slightly performancehit.
* No need to set when `returnPromise` is set --> returnPromise always handles the transitionFix.
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit
* @return {Promise|this} In case `returnPromise` is set, a Promise returns with the next handles:
* <ul>
* <li>cancel() {Promise}</li>
* <li>freeze() {Promise}</li>
* <li>unfreeze()</li>
* <li>finish() {Promise}</li>
* </ul>
* These handles resolve with the `elapsed-time` as first argument of the callbackFn
* @since 0.0.1
*/
ElementPrototype.removeClass = function(className, returnPromise, transitionFix, silent) {
var instance = this,
prevSuppress = DOCUMENT._suppressMutationEvents || false,
transPromise, returnValue;
silent && DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
transPromise = (returnPromise || transitionFix) && getClassTransPromise(instance, REMOVE, className);
returnValue = returnPromise ? transPromise : instance;
transPromise || instance.getClassList().remove(className);
if (silent && DOCUMENT.suppressMutationEvents) {
if (returnValue===instance) {
DOCUMENT.suppressMutationEvents(prevSuppress);
}
else {
returnValue.finally(function() {
DOCUMENT.suppressMutationEvents(prevSuppress);
});
}
}
return returnValue;
};
/**
* Removes data specified by `key` that was set by using `setData()`.
* When no arguments are passed, all node-data (key-value pairs) will be removed.
*
* @method removeData
* @param [key] {string} name of the key, when not set, all data is removed
* @param [deep] {Boolean} whether to set the data to all descendants recursively
* @chainable
* @since 0.0.1
*/
ElementPrototype.removeData = function(key, deep) {
var instance = this,
vnode = instance.vnode;
if (vnode._data) {
if (key) {
delete vnode._data[key];
}
else {
// we cannot just redefine _data, for it is set as readonly
vnode._cleanData();
if (deep) {
instance.getChildren().forEach(function(element) {
element.removeData(key, true);
});
}
}
}
return instance;
};
/**
* Removes the Elment's `id`.
*
* @method removeId
* @chainable
* @since 0.0.1
*/
ElementPrototype.removeId = function() {
return this.removeAttr('id');
};
/**
* Removes a css-property (inline) out of the Element.
* No need to use camelCase.
*
* @method removeInlineStyle
* @param cssProperty {String} the css-property to remove
* @param [pseudo] {String} to look inside a pseudo-style
* @param [returnPromise] {Boolean} whether to return a Promise instead of `this`, which might be useful in case of
* transition-properties. The promise will fullfil when the transition is ready, or immediately when no transitioned.
* @chainable
* @since 0.0.1
*/
ElementPrototype.removeInlineStyle = function(cssProperty, pseudo, returnPromise) {
return this.removeInlineStyles({property: cssProperty, pseudo: pseudo}, returnPromise);
};
/**
* Removes multiple css-properties (inline) out of the Element. You need to supply an Array of Objects, with the properties:
* <ul>
* <li>property {String}</li>
* <li>pseudo {String}</li>
* <ul>
* No need to use camelCase.
*
* @method removeInlineStyles
* @param cssProperties {Array|Object} Array of objects, Strings (or 1 Object/String).
* When String, then speduo is considered as undefined. When `Objects`, they need the properties:
* <ul>
* <li>property {String}</li>
* <li>pseudo {String}</li>
* <ul>
* @param [returnPromise] {Boolean} whether to return a Promise instead of `this`, which might be useful in case of
* transition-properties. The promise will fullfil when the transition is ready, or immediately when no transitioned.
* @chainable
* @since 0.0.1
*/
ElementPrototype.removeInlineStyles = function(cssProperties, returnPromise) {
// There will be 3 sets of styles:
// `fromStyles` --> the current styles, only exactly calculated -without `auto`- (that is, for the transitioned properties)
// `toStylesExact` --> the new styles, exactly calculated -without `auto`- (that is, for the transitioned properties)
// `vnodeStyles` --> the new styles as how they should be in the end (f.i. with `auto`)
var instance = this,
vnode = instance.vnode,
removed = [],
transCount = 0,
transitionProperties = {},
maxtranstime = 0,
needSync, prop, styles, i, len, item, hasTransitionedStyle, promise, vnodeStyles,
pseudo, group, clonedElement, fromStyles, toStylesExact, value, transproperty, transtime;
Array.isArray(cssProperties) || (cssProperties=[cssProperties]);
cssProperties = getVendorCSS(cssProperties);
len = cssProperties.length;
vnodeStyles = vnode.styles;
for (i=0; i<len; i++) {
item = cssProperties[i];
if (typeof item==='string') {
item = cssProperties[i] = {
property: item
};
}
pseudo = item.pseudo;
group = pseudo || 'element';
styles = vnodeStyles[group];
if (styles) {
prop = item.property;
// if property is vendor-specific transition, or transform, than we reset it to the current vendor
if (styles[prop]) {
fromStyles || (fromStyles=vnodeStyles.deepClone());
needSync = true;
if ((prop!==VENDOR_TRANSITION_PROPERTY) && instance.hasTransition(prop, pseudo)) {
// store the calculated value:
fromStyles[group] || (fromStyles[group]={});
(prop===VENDOR_TRANSFORM_PROPERTY) || (fromStyles[group][prop]=instance.getStyle(prop, group));
hasTransitionedStyle = true;
removed[removed.length] = {
group: group,
property: prop,
pseudo: pseudo
};
}
delete styles[prop];
(styles.size()===0) && (delete vnode.styles[pseudo || 'element']);
}
}
}
RUNNING_ON_NODE && (hasTransitionedStyle=false);
if (hasTransitionedStyle) {
// fix the current style with what is actual calculated:
vnode.styles = fromStyles; // exactly styles, so we can transition well
instance.setClass(NO_TRANS);
instance.setAttr(STYLE, vnode.serializeStyles());
async(function() {
// needs to be done in the next eventcyle, otherwise webkit-browsers miscalculate the syle (with transition on)
instance.removeClass(NO_TRANS);
});
// now calculate the final value
clonedElement = instance.cloneNode(true);
toStylesExact = vnodeStyles.deepClone();
clonedElement.vnode.styles = toStylesExact;
clonedElement.setClass(INVISIBLE_UNFOCUSABLE);
clonedElement.setAttr(STYLE, clonedElement.vnode.serializeStyles());
DOCUMENT.body.append(clonedElement);
// clonedElement has `vnodeStyles`, but we change them into `toStylesExact`
len = removed.length;
for (i=0; i<len; i++) {
item = removed[i];
prop = item.property;
group = item.pseudo || 'element';
if (!NON_CLONABLE_STYLES[prop]) {
value = (prop===VENDOR_TRANSFORM_PROPERTY) ? clonedElement.getInlineStyle(prop, item.pseudo) : clonedElement.getStyle(prop, item.pseudo);
if (value) {
toStylesExact[group] || (toStylesExact[group]={});
toStylesExact[group][prop] = value;
}
}
// look if we really have a change in the value:
if (toStylesExact[group] && (toStylesExact[group][prop]!==fromStyles[group][prop])) {
transproperty = instance.getTransition(prop, (group==='element') ? null : group);
if (transproperty) {
transtime = transproperty.delay+transproperty.duration;
maxtranstime = Math.max(maxtranstime, transtime);
if (transtime>0) {
transCount++;
// TODO: transitionProperties supposes that we DO NOT have pseudo transitions!
// as soon we do, we need to split this object for each 'group'
transitionProperties[prop] = true;
}
}
}
}
hasTransitionedStyle = (transCount>0);
clonedElement.remove();
}
if (needSync) {
if (returnPromise || hasTransitionedStyle) {
promise = window.Promise.manage();
// need to call `setAttr` in a next event-cycle, otherwise the eventlistener made
// by `getTransPromise gets blocked.
async(function() {
if (hasTransitionedStyle) {
// reset
vnode.styles = toStylesExact;
promise.then(function() {
vnode.styles = vnodeStyles; // finally values, not exactly calculated, but as is passed through
instance.setClass(NO_TRANS);
instance.setAttr(STYLE, vnode.serializeStyles());
}).finally(function() {
async(function() {
instance.removeClass(NO_TRANS);
// webkit browsers seems to need to recalculate their set width:
instance.getBoundingClientRect();
});
});
}
else {
vnode.styles = vnodeStyles; // finally values, not exactly calculated, but as is passed through
}
getTransPromise(instance, hasTransitionedStyle, null, transCount, transitionProperties, maxtranstime).then(
promise.fulfill
).catch(promise.reject);
instance.setAttr(STYLE, vnode.serializeStyles());
});
}
else {
vnode.styles = vnodeStyles; // finally values, not exactly calculated, but as is passed through
instance.setAttr(STYLE, vnode.serializeStyles());
// webkit browsers seems to need to recalculate their set width:
instance.getBoundingClientRect();
}
}
// else
return returnPromise ? (promise || window.Promise.resolve()) : instance;
};
/**
* Removes a subtype `transform`-css-property of (inline) out of the Element.
* This way you can sefely remove partial `transform`-properties while remaining the
* other inline `transform` css=properties.
*
* See more about tranform-properties: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
*
* @method removeInlineTransition
* @param transitionProperty {String} the css-transform property to remove
* @param [pseudo] {String} to look inside a pseudo-style
* @chainable
* @since 0.0.1
*/
ElementPrototype.removeInlineTransition = function(transitionProperty, pseudo) {
return this.removeInlineTransitions({property: transitionProperty, pseudo: pseudo});
};
/**
* Removes multiple subtype `transform`-css-property of (inline) out of the Element.
* This way you can sefely remove partial `transform`-properties while remaining the
* other inline `transform` css=properties.
* You need to supply an Array of Objects, with the properties:
* <ul>
* <li>property {String}</li>
* <li>pseudo {String}</li>
* <ul>
*
* See more about tranform-properties: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
*
* @method removeInlineTransitions
* @param transitionProperties {Array|Object} the css-transform properties to remove
* @chainable
* @since 0.0.1
*/
ElementPrototype.removeInlineTransitions = function(transitionProperties) {
var instance = this,
vnode = instance.vnode,
styles = vnode.styles,
groupStyle, transitionStyles, i, len, item, needSync, transitionProperty, pseudo;
if (styles) {
Array.isArray(transitionProperties) || (transitionProperties=[transitionProperties]);
transitionProperties = getVendorCSS(transitionProperties);
len = transitionProperties.length;
for (i=0; i<len; i++) {
item = transitionProperties[i];
pseudo = item.pseudo;
groupStyle = styles && styles[pseudo || 'element'];
transitionStyles = groupStyle && groupStyle[VENDOR_TRANSITION_PROPERTY];
if (transitionStyles) {
transitionProperty = item.property;
if (transitionStyles[transitionProperty]) {
delete transitionStyles[transitionProperty];
(transitionStyles.size()===0) && (delete groupStyle[VENDOR_TRANSITION_PROPERTY]);
(styles.size()===0) && (delete vnode.styles[pseudo || 'element']);
needSync = true;
}
}
}
}
needSync && instance.setAttr(STYLE, vnode.serializeStyles());
return instance;
};
/**
* Replaces the Element with a new Element.
*
* @method replace
* @param content {Element|Element|ElementArray|String} content to replace
* @param [escape] {Boolean} whether to insert `escaped` content, leading it into only text inserted
* @return {Element} the created Element (or the last when multiple)
* @since 0.0.1
*/
ElementPrototype.replace = function(newElement, escape) {
var instance = this,
vnode = instance.vnode,
previousVNode = vnode.vPrevious,
vParent = vnode.vParent,
createdElement;
createdElement = previousVNode ? vParent.domNode.append(newElement, escape, previousVNode.domNode) : vParent.domNode.prepend(newElement, escape);
instance.setClass(HIDDEN);
instance.remove();
return createdElement;
};
/**
* Replaces the Element's child-Element with a new Element.
*
* @method replaceChild
* @param newElement {Element} the new Element
* @param oldVChild {Element} the Element to be replaced
* @param [escape] {Boolean} whether to insert `escaped` content, leading it into only text inserted
* @return {Element} the Element that was removed (equals oldVChild)
* @since 0.0.1
*/
ElementPrototype._replaceChild = ElementPrototype.replaceChild;
ElementPrototype.replaceChild = function(newDomNode, oldDomNode, escape) {
return oldDomNode.replace(newDomNode, escape);
};
/**
* Replaces the className of the Element with a new className.
* If the previous className is not available, the new className is set nevertheless.
*
* @method replaceClass
* @param prevClassName {String} the className to be replaced
* @param newClassName {String} the className to be set
* @param [force ] {Boolean} whether the new className should be set, even is the previous className isn't there
* @param [returnPromise] {Boolean} whether to return a Promise instead of `this`, which might be useful in case of
* transition-properties. The promise will fullfil when the transition is ready, or immediately when no transitioned.
* @param [transitionFix] set this to `true` if you experience transition-problems due to wrong calculated css (mostly because of the `auto` value)
* Setting this parameter, will calculate the true css of the transitioned properties and set this temporarely inline, to fix the issue.
* Don't use it when not needed, it has a slightly performancehit.
* No need to set when `returnPromise` is set --> returnPromise always handles the transitionFix.
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit
* @return {Promise|this} In case `returnPromise` is set, a Promise returns with the next handles:
* <ul>
* <li>cancel() {Promise}</li>
* <li>freeze() {Promise}</li>
* <li>unfreeze()</li>
* <li>finish() {Promise}</li>
* </ul>
* These handles resolve with the `elapsed-time` as first argument of the callbackFn
* @since 0.0.1
*/
ElementPrototype.replaceClass = function(prevClassName, newClassName, force, returnPromise, transitionFix, silent) {
var instance = this,
prevSuppress = DOCUMENT._suppressMutationEvents || false,
transPromise, returnValue;
silent && DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
transPromise = (returnPromise || transitionFix) && getClassTransPromise(instance, REPLACE, newClassName, prevClassName, force);
if (force || instance.hasClass(prevClassName)) {
returnValue = returnPromise ? transPromise : instance;
transPromise || instance.removeClass(prevClassName).setClass(newClassName);
return returnValue;
}
if (silent && DOCUMENT.suppressMutationEvents) {
if (returnValue===instance) {
DOCUMENT.suppressMutationEvents(prevSuppress);
}
else {
returnValue.finally(function() {
DOCUMENT.suppressMutationEvents(prevSuppress);
});
}
}
return returnPromise ? window.Promise.resolve() : instance;
};
/**
* Scrolls the content of the Element into the specified scrollposition.
* Only available when the Element has overflow.
*
* @method scrollTo
* @param x {Number} left-offset in pixels
* @param y {Number} top-offset in pixels
* @chainable
* @since 0.0.1
*/
ElementPrototype.scrollTo = function(x, y) {
var instance = this;
instance.scrollLeft = x;
instance.scrollTop = y;
return instance;
};
/**
* Sets the attribute on the Element with the specified value.
*
* Alias for setAttribute(), BUT differs in a way that setAttr is chainable, setAttribute is not.
*
* @method setAttr
* @param attributeName {String}
* @param value {Any} the value that belongs to `key`
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit
* @chainable
* @since 0.0.1
*/
ElementPrototype.setAttr = function(attributeName, value, silent) {
var instance = this;
instance.setAttribute(attributeName, value, silent);
return instance;
};
/**
* Sets the attribute on the Element with the specified value.
*
* Alias for setAttr(), BUT differs in a way that setAttr is chainable, setAttribute is not.
*
* @method setAttribute
* @param attributeName {String}
* @param value {String} the value for the attributeName
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit
*/
ElementPrototype._setAttribute = ElementPrototype.setAttribute;
ElementPrototype.setAttribute = function(attributeName, value, silent) {
var instance = this,
prevSuppress = DOCUMENT._suppressMutationEvents || false,
vnode = instance.vnode;
(value==='') && (value=null);
silent && DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
((value!==null) && (value!==undefined)) ? vnode._setAttr(attributeName, value) : vnode._removeAttr(attributeName);
silent && DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(prevSuppress);
};
/**
* Sets the attribute on the Element with the specified value inside a specified namespace
*
* @method setAttributeNS
* @param nameSpace {String} the namespace where to attribuyte should be set in
* @param attributeName {String}
* @param value {String} the value for the attributeName
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit
*/
ElementPrototype._setAttributeNS = ElementPrototype.setAttributeNS;
ElementPrototype.setAttributeNS = function(nameSpace, attributeName, value, silent) {
this.setAttribute((nameSpace ? nameSpace+':' : '')+attributeName, value, silent);
};
/**
* Sets multiple attributes on the Element with the specified value.
* The argument should be one ore more Objects with the properties: `name` and `value`
*
* @example
* instance.setAttrs([
* {name: 'tabIndex', value: '0'},
* {name: 'style', value: 'color: #000;'}
* ]);
*
* @method setAttrs
* @param attributeData {Array|Object}
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit
* @chainable
* @since 0.0.1
*/
ElementPrototype.setAttrs = function(attributeData, silent) {
var instance = this;
Array.isArray(attributeData) || (attributeData=[attributeData]);
attributeData.forEach(function(item) {
instance.setAttribute(item.name, item.value, silent);
});
return instance;
};
/**
* Adds a class to the Element. If the class already exists it won't be duplicated.
*
* @method setClass
* @param className {String|Array} className to be added, may be an array of classNames
* @param [returnPromise] {Boolean} whether to return a Promise instead of `this`, which might be useful in case of
* transition-properties. The promise will fullfil when the transition is ready, or immediately when no transitioned.
* @param [transitionFix] set this to `true` if you experience transition-problems due to wrong calculated css (mostly because of the `auto` value)
* Setting this parameter, will calculate the true css of the transitioned properties and set this temporarely inline, to fix the issue.
* Don't use it when not needed, it has a slightly performancehit.
* No need to set when `returnPromise` is set --> returnPromise always handles the transitionFix.
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit
* @return {Promise|this} In case `returnPromise` is set, a Promise returns with the next handles:
* <ul>
* <li>cancel() {Promise}</li>
* <li>freeze() {Promise}</li>
* <li>unfreeze()</li>
* <li>finish() {Promise}</li>
* </ul>
* These handles resolve with the `elapsed-time` as first argument of the callbackFn
* @since 0.0.1
*/
ElementPrototype.setClass = function(className, returnPromise, transitionFix, silent) {
var instance = this,
prevSuppress = DOCUMENT._suppressMutationEvents || false,
transPromise, returnValue;
silent && DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
transPromise = (returnPromise || transitionFix) && getClassTransPromise(instance, SET, className);
returnValue = returnPromise ? transPromise : instance;
transPromise || instance.getClassList().add(className);
if (silent && DOCUMENT.suppressMutationEvents) {
if (returnValue===instance) {
DOCUMENT.suppressMutationEvents(prevSuppress);
}
else {
returnValue.finally(function() {
DOCUMENT.suppressMutationEvents(prevSuppress);
});
}
}
return returnValue;
};
/**
* Stores arbitary `data` at the Element (actually at vnode). This has nothing to do with node-attributes whatsoever,
* it is just a way to bind any data to the specific Element so it can be retrieved later on with `getData()`.
*
* @method setData
* @param key {string} name of the key
* @param value {Any} the value that belongs to `key`
* @param [deep] {Boolean} whether to set the data to all descendants recursively
* @chainable
* @since 0.0.1
*/
ElementPrototype.setData = function(key, value, deep) {
var instance = this,
vnode = instance.vnode;
if (value!==undefined) {
vnode._data || Object.protectedProp(vnode, '_data', {});
vnode._data[key] = value;
if (deep) {
instance.getChildren().forEach(function(element) {
element.setData(key, value, true);
});
}
}
return instance;
};
/**
* Sets the innerHTML of both the vnode as well as the representing dom-node.
* Goes through the vdom, so it's superfast.
*
* Use this method instead of `innerHTML`
*
* Syncs with the DOM.
*
* @method setHTML
* @param val {String} the new value to be set
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit
* @param [allowScripts=false] {Boolean} whether scripts are allowed --> these should be defined with `xscript` instead of `script`
* @chainable
* @since 0.0.1
*/
ElementPrototype.setHTML = function(val, silent, allowScripts) {
var instance = this,
prevSuppress = DOCUMENT._suppressMutationEvents || false;
silent && DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
instance.vnode.setHTML(val, null, allowScripts);
silent && DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(prevSuppress);
return instance;
};
/**
* Sets the Elments `id`
*
* @method setId
* @param val {String} Elements new `id`
* @chainable
* @since 0.0.1
*/
ElementPrototype.setId = function(val) {
return this.setAttr('id', val);
};
/**
* Sets a css-property (inline) for the Element.
*
* Note1: Do not use vendor-specific properties, but general (like `transform` instead of `-webkit-transform`)
* This method will use the appropriate css-property.
* Note2: no need to camelCase cssProperty: both `margin-left` as well as `marginLeft` are fine
*
* @method setInlineStyle
* @param cssProperty {String} the css-property to be set
* @param value {String} the css-value
* @param [pseudo] {String} to look inside a pseudo-style
* @param [returnPromise] {Boolean} whether to return a Promise instead of `this`, which might be useful in case of
* transition-properties. The promise will fullfil when the transition is ready, or immediately when no transitioned.
* @return {Promise|this}
* @since 0.0.1
*/
ElementPrototype.setInlineStyle = function(cssProperty, value, pseudo, returnPromise) {
if (typeof pseudo==='boolean') {
returnPromise = pseudo;
pseudo = null;
}
return this.setInlineStyles([{property: cssProperty, value: value, pseudo: pseudo}], returnPromise);
};
/**
* Sets multiple css-properties (inline) for the Element at once.
*
* Note1: Do not use vendor-specific properties, but general (like `transform` instead of `-webkit-transform`)
* This method will use the appropriate css-property.
* Note2: no need to camelCase cssProperty: both `margin-left` as well as `marginLeft` are fine
*
* @method setInlineStyles
* @param cssProperties {Array|Object} the css-properties to be set, specified as an Array of Objects, or 1 Object.
* The objects should have the next properties:
* <ul>
* <li>property {String}</li>
* <li>value {String}</li>
* <li>pseudo {String} (optional) --> not: not supported yet in browsers</li>
* </ul>
* @param [returnPromise] {Boolean} whether to return a Promise instead of `this`, which might be useful in case of
* transition-properties. The promise will fullfil when the transition is ready, or immediately when no transitioned.
* @return {Promise|this}
* @since 0.0.1
*/
ElementPrototype.setInlineStyles = function(cssProperties, returnPromise) {
// There will be 3 sets of styles:
// `fromStyles` --> the current styles, only exactly calculated -without `auto`- (that is, for the transitioned properties)
// `toStylesExact` --> the new styles, exactly calculated -without `auto`- (that is, for the transitioned properties)
// `vnodeStyles` --> the new styles as how they should be in the end (f.i. with `auto`)
var instance = this,
vnode = instance.vnode,
transitionedProps = [],
transCount = 0,
maxtranstime = 0,
transitionProperties = {},
// third argument is a hidden feature --> used by getClassTransPromise()
avoidBackup = arguments[2],
styles, group, i, len, item, promise, hasTransitionedStyle, property, hasChanged, transtime,
pseudo, fromStyles, value, vnodeStyles, toStylesExact, clonedElement, transproperty;
// if there is a class-transition going on (initiated by getClassTransPromise),
// the we might need to update the internal bkpNode:
if (!avoidBackup && vnode._data) {
// there might be more bkpNodes, so we need to loop through the data:
vnode._data.each(function(bkpNode, key) {
if (key.startsWith('bkpNode')) {
bkpNode.setInlineStyles(cssProperties, null, true);
}
});
}
Array.isArray(cssProperties) || (cssProperties=[cssProperties]);
cssProperties = getVendorCSS(cssProperties);
len = cssProperties.length;
vnode.styles || (vnode.styles={});
vnodeStyles = vnode.styles;
// Both `from` and `to` ALWAYS need to be set to their calculated value --> this makes transition
// work with `auto`, or when the page isn't completely loaded
// First: backup the actual style:
fromStyles = vnodeStyles.deepClone();
for (i=0; i<len; i++) {
item = cssProperties[i];
pseudo = item.pseudo;
group = pseudo || 'element';
vnodeStyles[group] || (vnodeStyles[group]={});
styles = vnodeStyles[group];
property = fromCamelCase(item.property);
value = item.value;
(property===VENDOR_TRANSITION_PROPERTY) && (value=extractor.toTransitionObject(value));
if (value===undefined) {
delete styles[property];
}
else {
styles[property] = value;
}
if ((property!==VENDOR_TRANSITION_PROPERTY) && (instance.getStyle(property, pseudo)!==value) && instance.hasTransition(property, pseudo)) {
fromStyles[group] || (fromStyles[group]={});
(property===VENDOR_TRANSFORM_PROPERTY) || (fromStyles[group][property]=instance.getStyle(property, pseudo));
if (fromStyles[group][property]!==value) {
transproperty = instance.getTransition(property, (group==='element') ? null : group);
if (transproperty) {
transtime = transproperty.delay+transproperty.duration;
maxtranstime = Math.max(maxtranstime, transtime);
if (transtime>0) {
hasTransitionedStyle = true;
transCount++;
// TODO: transitionProperties supposes that we DO NOT have pseudo transitions!
// as soon we do, we need to split this object for each 'group'
transitionProperties[property] = true;
transitionedProps[transitionedProps.length] = {
group: group,
property: property,
value: value,
pseudo: pseudo
};
}
}
}
}
}
RUNNING_ON_NODE && (hasTransitionedStyle=false);
if (hasTransitionedStyle) {
// we forced set the exact initial css inline --> this is the only way to make a right transition
// under all circumstances
toStylesExact = vnodeStyles.deepClone();
clonedElement = instance.cloneNode(true); // cloned with `vnodeStyles`
clonedElement.vnode.styles = toStylesExact;
// fix the current style with what is actual calculated:
vnode.styles = fromStyles; // exactly styles, so we can transition well
instance.setClass(NO_TRANS);
instance.setAttr(STYLE, vnode.serializeStyles());
async(function() {
// needs to be done in the next eventcyle, otherwise webkit-browsers miscalculate the syle (with transition on)
instance.removeClass(NO_TRANS);
});
// clonedElement has `vnodeStyles`, but we change them into `toStylesExact`
clonedElement.setClass(INVISIBLE_UNFOCUSABLE);
clonedElement.setAttr(STYLE, clonedElement.vnode.serializeStyles());
DOCUMENT.body.append(clonedElement);
// now calculate the `transition` styles and store them in the css-property of `toStylesExact`:
len = transitionedProps.length;
hasChanged = false;
for (i=0; i<len; i++) {
item = transitionedProps[i];
property = item.property;
group = item.pseudo || 'element';
if (!NON_CLONABLE_STYLES[property]) {
value = (property===VENDOR_TRANSFORM_PROPERTY) ? clonedElement.getInlineStyle(property, item.pseudo) : clonedElement.getStyle(property, item.pseudo);
if (value) {
toStylesExact[group] || (toStylesExact[group]={});
toStylesExact[group][property] = value;
}
}
// look if we really have a change in the value:
if (!hasChanged && toStylesExact[group]) {
hasChanged = (toStylesExact[group][property]!==fromStyles[group][property]);
}
}
clonedElement.remove();
hasTransitionedStyle = hasChanged;
}
RUNNING_ON_NODE && (hasTransitionedStyle=false);
if (returnPromise || hasTransitionedStyle) {
promise = window.Promise.manage();
// need to call `setAttr` in a next event-cycle, otherwise the eventlistener made
// by `getTransPromise gets blocked.
async(function() {
if (hasTransitionedStyle) {
// reset
vnode.styles = toStylesExact;
promise.then(function() {
vnode.styles = vnodeStyles; // finally values, not exactly calculated, but as is passed through
instance.setClass(NO_TRANS);
instance.setAttr(STYLE, vnode.serializeStyles());
}).finally(function() {
async(function() {
// needs to be done in the next eventcyle, otherwise webkit-browsers miscalculate the syle (with transition on)
instance.removeClass(NO_TRANS);
// webkit browsers seems to need to recalculate their set width:
instance.getBoundingClientRect();
});
});
}
else {
vnode.styles = vnodeStyles; // finally values, not exactly calculated, but as is passed through
}
getTransPromise(instance, hasTransitionedStyle, null, transCount, transitionProperties, maxtranstime).then(
function() {
promise.fulfill();
}
).catch(promise.reject);
instance.setAttr(STYLE, vnode.serializeStyles());
});
return returnPromise ? promise : instance;
}
// else
vnode.styles = vnodeStyles; // finally values, not exactly calculated, but as is passed through
instance.setAttr(STYLE, vnode.serializeStyles());
// webkit browsers seems to need to recalculate their set width:
instance.getBoundingClientRect();
return instance;
};
/**
* Sets a transform-css-property (inline) for the Element.
*
* See more about transitions: https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Using_CSS_transitions
*
* @method setStyle
* @param setInlineTransition {String} the css-property to be set, f.e. `translateX`
* @param duration {Number} the duration in seconds (may be a broken number, like `0.5`)
* @param [timingFunction] {String} See https://developer.mozilla.org/en-US/docs/Web/CSS/transition-timing-function
* @param delay {Number} the delay in seconds (may be a broken number, like `0.5`)
* @param [pseudo] {String} to look inside a pseudo-style
* @chainable
* @since 0.0.1
*/
ElementPrototype.setInlineTransition = function(transitionProperty, duration, timingFunction, delay, pseudo) {
// transition-example: transition: width 2s, height 2s, transform 2s;
return this.setInlineTransitions({property: transitionProperty, duration: duration, timingFunction: timingFunction, delay: delay, pseudo: pseudo});
};
/**
* Sets a transform-css-property (inline) for the Element.
*
* See more about transitions: https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Using_CSS_transitions
*
* @method setStyle
* @param transitionProperties {Array} the css-transition-properties to be set, specified as an Array of Objects.
* The objects should have the next properties:
* <ul>
* <li>property {String}</li>
* <li>duration {Number}</li>
* <li>timingFunction {String} (optional)</li>
* <li>delay {Number} (optional)</li>
* <li>pseudo {String} (optional)</li>
* </ul>
* @param [pseudo] {String} to look inside a pseudo-style
* @chainable
* @since 0.0.1
*/
ElementPrototype.setInlineTransitions = function(transitionProperties) {
// transition-example: transition: width 2s, height 2s, transform 2s;
var instance = this,
vnode = instance.vnode,
transitionStyles, transitionProperty, group, trans, i, len, item;
Array.isArray(transitionProperties) || (transitionProperties=[transitionProperties]);
transitionProperties = getVendorCSS(transitionProperties);
len = transitionProperties.length;
vnode.styles || (vnode.styles={});
for (i=0; i<len; i++) {
item = transitionProperties[i];
if (item.property) {
group = item.pseudo || 'element';
vnode.styles[group] || (vnode.styles[group]={});
vnode.styles[group][VENDOR_TRANSITION_PROPERTY] || (vnode.styles[group][VENDOR_TRANSITION_PROPERTY]={});
transitionStyles = vnode.styles[group][VENDOR_TRANSITION_PROPERTY];
transitionProperty = fromCamelCase(item.property);
trans = transitionStyles[transitionProperty] = {
duration: item.duration
};
item.timingFunction && (trans.timingFunction=item.timingFunction);
item.delay && (trans.delay=item.delay);
}
}
instance.setAttr(STYLE, vnode.serializeStyles());
return instance;
};
/**
* Gets or sets the outerHTML of both the Element as well as the representing dom-node.
* Goes through the vdom, so it's superfast.
*
* Use this property instead of `outerHTML`
*
* Syncs with the DOM.
*
* @method setOuterHTML
* @param val {String} the new value to be set
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit
* @chainable
* @since 0.0.1
*/
ElementPrototype.setOuterHTML = function(val, silent) {
var instance = this,
prevSuppress = DOCUMENT._suppressMutationEvents || false;
silent && DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
instance.vnode.outerHTML = val;
silent && DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(prevSuppress);
return instance;
};
/**
* Sets the innerContent of the Element as plain text.
* Goes through the vdom, so it's superfast.
*
* Use this method instead of `textContent`
*
* Syncs with the DOM.
*
* @method setText
* @param val {String} the textContent to be set
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit
* @chainable
* @since 0.0.1
*/
ElementPrototype.setText = function(val, silent) {
var instance = this,
prevSuppress = DOCUMENT._suppressMutationEvents || false;
silent && DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
instance.vnode.textContent = val;
silent && DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(prevSuppress);
return instance;
};
/**
* Sets the value of the following Elements:
*
* <ul>
* <li>input</li>
* <li>textarea</li>
* <li>select</li>
* <li>any container that is `contenteditable`</li>
* </ul>
*
* Will emit a `valuechange`-event when a new value is set and ITSA's `event`-module is active.
*
* @method setValue
* @param val {String} thenew value to be set
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit. Only appropriate for contenteditable nodes.
* @chainable
* @since 0.0.1
*/
ElementPrototype.setValue = function(val, silent) {
var instance = this,
prevVal = instance.value,
contenteditable = instance.vnode.attrs.contenteditable,
// cautious: input and textarea must be accessed by their propertyname:
// input.getAttribute('value') would return the defualt-value instead of actusl
// and textarea.getAttribute('value') doesn't exist
editable = contenteditable && (contenteditable!=='false'),
tag, i, option, len, vChildren;
if (editable) {
// no need to compare with current html --> when vdom is working, only differences are set
instance.setHTML(val, silent);
}
else {
tag = instance.getTagName();
if ((tag==='INPUT') || (tag==='TEXTAREA')) {
// don't update when not needed: we don't want to reposition the cursor
(instance.value!==val) && (instance.value=val);
}
else if (tag==='SELECT') {
vChildren = instance.vnode.vChildren;
len = vChildren.length;
for (i=0; i<len; i++) {
option = vChildren[i];
if (option.attrs.value === val) {
instance.selectedIndex = i;
break;
}
}
}
}
// if `document._emitVC` is available, then invoke it to emit the `valuechange`-event
/**
* @event valuechange
* @param e.value {String} new value
* @param e.sourceTarget {Element} Element whare the valuechange occured
*/
DOCUMENT._emitVC && (prevVal!==val) && DOCUMENT._emitVC(instance, val);
return instance;
};
/**
* Set the position of an html element in page coordinates.
* The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
*
* If the Element has the attribute `xy-constrian` set, then its position cannot exceed any matching container it lies within.
*
* @method setXY
* @param x {Number} x-value for new position (coordinates are page-based)
* @param y {Number} y-value for new position (coordinates are page-based)
* @param [constrain] {'window', Element, Object, String}
* <ul>
* <li><b>'window'</b> to constrain to the visible window</li>
* <li><b>Element</b> to constrain to a specified Element</li>
* <li><b>Object</b> to constrain to an object with the properties: {x, y, w, h} where x and y are absolute pixels of the document
* (like calculated with getX() and getY()).</li>
* <li><b>String</b> to constrain to a specified css-selector, which should be an ancestor</li>
* </ul>
* @param [notransition=false] {Boolean} set true if you are sure positioning is without transition.
* this isn't required, but it speeds up positioning. Only use when no transition is used:
* when there is a transition, setting this argument `true` would miscalculate the position.
* The return-value will be `this` in case `notransition`===true, making setXY to be chainable.
* @return {Promise|this}
* @since 0.0.1
*/
ElementPrototype.setXY = function(x, y, constrain, notransition) {
console.log(NAME, 'setXY '+x+','+y);
var instance = this,
dif, constrainNode, parent, clone, promise,
containerTop, containerRight, containerLeft, containerBottom, requestedX, requestedY,
transObject, xtrans, ytrans, inlinePosition, globalPosition, invisibleClass;
// default position to relative: check first inlinestyle because this goes quicker
inlinePosition = instance.getInlineStyle(POSITION);
inlinePosition || (globalPosition=instance.getStyle(POSITION));
if ((inlinePosition==='static') || (inlinePosition==='fixed') || (globalPosition==='static') || (globalPosition==='fixed')) {
inlinePosition = 'relative';
instance.setInlineStyle(POSITION, inlinePosition);
}
invisibleClass = (inlinePosition==='absolute') ? INVISIBLE : INVISIBLE_RELATIVE;
// make sure it has sizes and can be positioned
instance.setClass([invisibleClass, BORDERBOX]);
(instance.getInlineStyle('display')==='none') && instance.setClass(BLOCK);
constrain || (constrain=instance.getAttr('constrain-selector'));
if (constrain) {
if (constrain==='window') {
containerLeft = window.getScrollLeft();
containerTop = window.getScrollTop();
containerRight = containerLeft + window.getWidth();
containerBottom = containerTop + window.getHeight();
}
else {
if (typeof constrain === STRING) {
constrainNode = instance.inside(constrain);
}
if (constrain.matchesSelector) {
// Element --> we need to search the rectangle
containerLeft = constrain.left + parseInt(constrain.getStyle(BORDER_LEFT_WIDTH), 10);
containerTop = constrain.top + parseInt(constrain.getStyle(BORDER_TOP_WIDTH), 10);
containerRight = containerLeft + constrain.scrollWidth;
containerBottom = containerTop + constrain.scrollHeight;
}
else {
containerLeft = constrain.x;
containerTop = constrain.y;
containerRight = constrain.x + constrain.w;
containerBottom = constrain.y + constrain.h;
}
}
if (typeof containerLeft === NUMBER) {
// found constrain, always redefine x and y
x = requestedX = (typeof x===NUMBER) ? x : instance.left;
if (requestedX<containerLeft) {
x = containerLeft;
}
else {
if ((requestedX+instance.offsetWidth)>containerRight) {
x = requestedX = containerRight - instance.offsetWidth;
}
// now we might need to reset to the left again:
(requestedX<containerLeft) && (x=containerLeft);
}
y = requestedY = (typeof y===NUMBER) ? y : instance.top;
if (requestedY<containerTop) {
y = containerTop;
}
else {
if ((requestedY+instance.offsetHeight)>containerBottom) {
y = requestedY = containerBottom - instance.offsetHeight;
}
// now we might need to reset to the top again:
(requestedY<containerTop) && (y=containerTop);
}
}
}
xtrans = (typeof x === NUMBER);
ytrans = (typeof y === NUMBER);
if (xtrans || ytrans) {
// check if there is a transition:
if (notransition) {
instance.setClass([NO_TRANS2, invisibleClass]);
transObject = [];
xtrans && (transObject[0]={property: LEFT, value: x + PX});
ytrans && (transObject[xtrans ? 1 : 0]={property: TOP, value: y + PX});
instance.setInlineStyles(transObject);
// reset transObject and maybe it will be filled when there is a difference
// between the set value and the true value (which could appear due to different `position` properties)
transObject = [];
if (xtrans) {
dif = (instance.left-x);
(dif!==0) && (transObject[0]={property: LEFT, value: (x - dif) + PX});
}
if (ytrans) {
dif = (instance.top-y);
(dif!==0) && (transObject[transObject.length]={property: TOP, value: (y - dif) + PX});
}
(transObject.length>0) && instance.setInlineStyles(transObject);
instance.removeClass([NO_TRANS2, invisibleClass]);
}
else {
// we will clone the node, make it invisible and without transitions and look what its correction should be
clone = instance.cloneNode();
clone.setClass([NO_TRANS2, invisibleClass]);
parent = instance.getParent() || DOCUMENT.body;
parent.prepend(clone, null, instance);
transObject = [];
xtrans && (transObject[0]={property: LEFT, value: x + PX});
ytrans && (transObject[xtrans ? 1 : 0]={property: TOP, value: y + PX});
clone.setInlineStyles(transObject);
// reset transObject and fill it with the final true values
transObject = [];
xtrans && (transObject[0]={property: LEFT, value: (2*x-clone.left) + PX});
ytrans && (transObject[xtrans ? 1 : 0]={property: TOP, value: (2*y-clone.top) + PX});
clone.remove();
promise = instance.setInlineStyles(transObject, true);
}
}
else if (!notransition) {
promise = window.Promise.resolve();
}
instance.removeClass([BLOCK, BORDERBOX, invisibleClass]);
return promise || instance;
};
/**
* Shows a previously hidden node.
* Shows immediately without `fade`, or will fade-in when fade is specified.
*
* @method show
* @param [fade] {Number} sec to fade-in (you may use `0.1`)
* @return {this|Promise} fulfilled when the element is ready showing up, or rejected when hidden again (using node.hide) before fully showed.
* @since 0.0.1
*/
ElementPrototype.show = function(duration, forceFull) {
var instance = this,
showPromise = instance.getData('_showNodeBusy'),
hidePromise = instance.getData('_hideNodeBusy'),
originalOpacity, hasOriginalOpacity, promise, freezedOpacity, finalValue;
instance.setData('nodeShowed', true); // for any routine who wants to know
originalOpacity = instance.getData('_showNodeOpacity');
if (!originalOpacity && !showPromise && !hidePromise) {
originalOpacity = instance.getInlineStyle('opacity');
instance.setData('_showNodeOpacity', originalOpacity);
}
hasOriginalOpacity = !!originalOpacity;
showPromise && showPromise.freeze();
if (hidePromise) {
hidePromise.freeze();
instance.removeData('_hideNodeBusy');
}
if (duration) {
instance.setInlineStyle('opacity', (instance.hasClass(HIDDEN) ? 0 : instance.getStyle('opacity')));
instance.removeClass(HIDDEN);
finalValue = (forceFull || !hasOriginalOpacity) ? 1 : originalOpacity;
if (showPromise || hidePromise) {
freezedOpacity = instance.getInlineStyle('opacity');
duration = (finalValue>0) ? Math.min(1, ((finalValue-freezedOpacity)/finalValue))*duration : 0;
}
promise = instance.transition({property: 'opacity', value: finalValue, duration: duration});
instance.setData('_showNodeBusy', promise);
promise.finally(function() {
if (!promise.cancelled && !promise.frozen) {
hasOriginalOpacity || instance.removeInlineStyle('opacity');
if (!forceFull || !hasOriginalOpacity) {
instance.removeData('_showNodeOpacity');
}
}
instance.removeData('_showNodeBusy');
});
return promise;
}
else {
async(function() {
(hasOriginalOpacity && !forceFull) ? instance.setInlineStyle('opacity', originalOpacity) : instance.removeInlineStyle('opacity');
instance.removeClass(HIDDEN);
});
return instance;
}
};
/**
* Transitions one ore more properties of the Element.
*
* @method toggleClass
* @param to {Array} the css-properties to be set, specified as an Array of Objects.
* The objects should have the next properties:
* <ul>
* <li>property {String}</li>
* <li>value {String}</li>
* <li>duration {Number} (optional)</li>
* <li>timingFunction {String} (optional)</li>
* <li>delay {String} (optional)</li>
* <li>pseudo {String} (optional) --> not: not supported yet in browsers</li>
* </ul>
* @param [from] {Array} starting the css-properties to be set, specified as an Array of Objects.
* If disguarded, then the current style is used as startingpoint. You may specify a subset of the `to`-properties.
* The objects should have the next properties:
* <ul>
* <li>property {String}</li>
* <li>value {String}</li>
* <li>duration {Number} (optional)</li>
* <li>timingFunction {String} (optional)</li>
* <li>delay {String} (optional)</li>
* <li>pseudo {String} (optional) --> not: not supported yet in browsers</li>
* </ul>
* @return {Promise} The promise has the handles:
* <ul>
* <li>cancel() {Promise}</li>
* <li>freeze() {Promise}</li>
* <li>unfreeze()</li>
* <li>finish() {Promise}</li>
* </ul>
* These handles resolve with the `elapsed-time` as first argument of the callbackFn
* @since 0.0.1
*/
ElementPrototype.transition = function(to, from) {
var instance = this,
currentInlineTransition, transitions, transitionRun, transitionError, promise, resolveHandle, initialStyle, time1, intermediateInvoked,
initialProperties, cleanup, getCurrentProperties, manipulated, getNoTransProp, transpromise, endIntermediate, time2;
to || (to={});
Array.isArray(to) || (to=[to]);
to = getVendorCSS(to);
transitions = Array.isArray(to) ? to.deepClone() : [to.shallowClone()];
time1 = Date.now();
// transitions = Array.isArray(to) ? to.deepClone() : [to.shallowClone()];
cleanup = function() {
currentInlineTransition = instance.getData('_bkpTransition');
currentInlineTransition ? instance.setInlineStyle(TRANSITION, currentInlineTransition) : instance.removeInlineStyle(TRANSITION);
instance.removeData('_bkpTransition');
instance.removeData('_readyOnRun');
Object.defineProperty(promise, 'isFulfilled', {
configurable: false,
enumerable: false,
writable: false,
value: true
});
};
getCurrentProperties = function() {
var props = [],
currentStyle = window.getComputedStyle(instance),
currentStyleBefore = window.getComputedStyle(instance, ':before'),
currentStyleAfter = window.getComputedStyle(instance, ':after');
to.each(function(value) {
var styles = (value.pseudo===':before') ? currentStyleBefore : ((value.pseudo===':after') ? currentStyleAfter : currentStyle),
property = value.property;
// if property is vendor-specific transition, or transform, than we reset it to the current vendor
props.push({
property: property,
value: styles[toCamelCase(property)]
});
});
return props;
};
getNoTransProp = function() {
var props = [];
transitions.forEach(function(item) {
props.push({
property: item.property,
duration: 0,
delay: 0
});
});
return props;
};
endIntermediate = function(type) {
intermediateInvoked = true;
if (!promise.isFulfilled) {
manipulated = true;
instance.setInlineTransitions(getNoTransProp());
instance.setInlineStyles((type==='cancelled') ? initialProperties : getCurrentProperties());
// also force to set the style on the node outside the vdom --> by forcing this
// we won't run into the situation where the vdom doesn't change the dom because the style didn';'t change:
instance._setAttribute(STYLE, instance.getAttr(STYLE));
switch (type) {
case 'cancelled':
// now cleanup inline style that wasn't there initially,
async(function() {
instance.setClass(NO_TRANS2);
instance.setAttr(STYLE, initialStyle);
instance.removeClass(NO_TRANS2);
});
cleanup();
break;
case 'frozen':
async(function() {
cleanup();
});
break;
case 'finished':
instance.setInlineStyles(to);
async(function() {
cleanup();
});
break;
}
Object.defineProperty(promise, type, {
configurable: false,
enumerable: false,
writable: false,
value: true
});
// prevent transitionpromise to set its own final values after finishing
// but only if it is already available:
transpromise && transpromise.reject();
resolveHandle && resolveHandle();
}
time2 || (time2=Date.now());
return new window.Promise(function(resolve) {
async(function() {
resolve(time2-time1);
});
});
};
promise = new window.Promise(function(resolve, reject) {
async(function() {
if (intermediateInvoked) {
reject();
return;
}
resolveHandle = resolve;
transitionRun = idGenerator('nodeTransition');
// only make ready on the last run
instance.setData('_readyOnRun', transitionRun);
if (from) {
instance.setClass(NO_TRANS2);
instance.setInlineStyles(from);
instance.removeClass(NO_TRANS2);
}
initialProperties = getCurrentProperties();
initialStyle = instance.getAttr(STYLE);
currentInlineTransition = instance.getData('_bkpTransition');
if (currentInlineTransition===undefined) {
currentInlineTransition = instance.getInlineStyle(TRANSITION) || null;
// `null` can be set as node-data, `undefined` connot
instance.setData('_bkpTransition', currentInlineTransition);
}
// we could use the `to` object and pass into `setInlineTransitions` directly,
// however, in case `duration` is not specified, we will define them to 1 sec.
// CAUTIOUS: the sum of `duration`+`delay` determines when the transition will be ready.
// This leads into separate transitions, we must prevent the promise to fulfill on the
// first tranition to be ready.
// Thus: we need to split every (`duration`+`delay`) group and give them each a separate setInlineStyle()-promise!
transitions.forEach(function(item) {
item.duration || (item.duration=1);
item.delay || (item.delay=0);
});
instance.setInlineTransitions(transitions);
transpromise = instance.setInlineStyles(to, true);
transpromise.catch(
function(err) {
transitionError = err;
return true; // fulfill the chain
}
).finally(
function() {
// to prevent `transitionend` events biting each other when chaining `transition`,
// and reset the inline transition in time,
// we need to resolve the Promise after the eventstack:
async(function() {
if (!manipulated && (instance.getData('_readyOnRun')===transitionRun)) {
cleanup();
// because cleanup does an async action (setInlineStyles), we will append the eventstack:
async(function() {
if (transitionError) {
reject(transitionError);
}
else {
time2 || (time2=Date.now());
resolve(time2-time1);
}
});
}
});
}
);
});
});
promise.cancel = function() {
return endIntermediate('cancelled');
};
promise.freeze = function() {
return endIntermediate('frozen');
};
promise.finish = function() {
return endIntermediate('finished');
};
return promise;
};
/**
* Toggles the className of the Element.
*
* @method toggleClass
* @param className {String|Array} className that should be toggled, may be an array of classNames
* @param forceState {Boolean} to force toggling into this specific state
* @param [returnPromise] {Boolean} whether to return a Promise instead of `this`, which might be useful in case of
* transition-properties. The promise will fullfil when the transition is ready, or immediately when no transitioned.
* @param [transitionFix] set this to `true` if you experience transition-problems due to wrong calculated css (mostly because of the `auto` value)
* Setting this parameter, will calculate the true css of the transitioned properties and set this temporarely inline, to fix the issue.
* Don't use it when not needed, it has a slightly performancehit.
* No need to set when `returnPromise` is set --> returnPromise always handles the transitionFix.
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit
* @return {Promise|this} In case `returnPromise` is set, a Promise returns with the next handles:
* <ul>
* <li>cancel() {Promise}</li>
* <li>freeze() {Promise}</li>
* <li>unfreeze()</li>
* <li>finish() {Promise}</li>
* </ul>
* These handles resolve with the `elapsed-time` as first argument of the callbackFn
* @since 0.0.1
*/
ElementPrototype.toggleClass = function(className, forceState, returnPromise, transitionFix, silent) {
var instance = this,
prevSuppress = DOCUMENT._suppressMutationEvents || false,
transPromise, returnValue;
silent && DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
transPromise = (returnPromise || transitionFix) && getClassTransPromise(instance, TOGGLE, className, forceState);
returnValue = returnPromise ? transPromise : instance;
transPromise || instance.getClassList().toggle(className, forceState);
if (silent && DOCUMENT.suppressMutationEvents) {
if (returnValue===instance) {
DOCUMENT.suppressMutationEvents(prevSuppress);
}
else {
returnValue.finally(function() {
DOCUMENT.suppressMutationEvents(prevSuppress);
});
}
}
return returnValue;
};
Object.defineProperties(ElementPrototype, {
/**
* Gets or set the height of the element in pixels. Included are padding and border, not any margins.
* By setting the argument `overflow` you get the total height, included the invisible overflow.
*
* The getter is calculating through `offsetHeight`, the setter will set inline css-style for the height.
*
* Values are numbers without unity.
*
* @property height
* @type {Number}
* @since 0.0.1
*/
height: {
get: function() {
return this.offsetHeight;
},
set: function(val) {
var instance = this,
dif;
instance.setClass(INVISIBLE);
instance.setInlineStyle(HEIGHT, val + PX);
dif = (instance.offsetHeight-val);
(dif!==0) && (instance.setInlineStyle(HEIGHT, (val - dif) + PX));
instance.removeClass(INVISIBLE);
}
},
/**
* Gets the x-position (in the DOCUMENT) of the element in pixels.
* DOCUMENT-related: regardless of the window's scroll-position.
*
* @property left
* @since 0.0.1
*/
left: {
get: function() {
return Math.round(this.getBoundingClientRect().left + window.getScrollLeft());
},
set: function(pixelsLeft) {
return this.setXY(pixelsLeft, null, null, true);
}
},
/**
* Gets the y-position (in the DOCUMENT) of the element in pixels.
* DOCUMENT-related: regardless of the window's scroll-position.
*
* @property top
* @since 0.0.1
*/
top: {
get: function() {
return Math.round(this.getBoundingClientRect().top + window.getScrollTop());
},
set: function(pixelsTop) {
return this.setXY(null, pixelsTop, null, true);
}
},
/**
* Gets or set the width of the element in pixels. Included are padding and border, not any margins.
* By setting the argument `overflow` you get the total width, included the invisible overflow.
*
* The getter is calculating through `offsetHeight`, the setter will set inline css-style for the width.
*
* Values are numbers without unity.
*
* @property width
* @type {Number}
* @since 0.0.1
*/
width: {
get: function() {
return this.offsetWidth;
},
set: function(val) {
var instance = this,
dif;
instance.setClass(INVISIBLE);
instance.setInlineStyle(WIDTH, val + PX);
dif = (instance.offsetWidth-val);
(dif!==0) && (instance.setInlineStyle(WIDTH, (val - dif) + PX));
instance.removeClass(INVISIBLE);
}
}
});
}(window.Element.prototype));
setupObserver = function() {
// configuration of the observer:
var observerConfig = {
attributes: true,
subtree: true,
characterData: true,
childList : true
};
(new window.MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
var node = mutation.target,
vnode = node.vnode,
type = mutation.type,
attribute = mutation.attributeName,
addedChildNodes = mutation.addedNodes,
removedChildNodes = mutation.removedNodes,
i, len, childDomNode, childVNode, index, vchildnode;
if (vnode && !vnode._nosync) {
if (type==='attributes') {
vnode.reloadAttr(attribute);
}
else if (type==='characterData') {
vnode.text = node.nodeValue;
}
else {
// remove the childNodes that are no longer there,
// but ONLY when they are not in the dom --> nodes might get
// replaced inside other nodes, which leads into 'remove'-observer,
// yet we still need them
len = removedChildNodes.length;
for (i=len-1; i>=0; i--) {
childDomNode = removedChildNodes[i];
childVNode = childDomNode.vnode;
// need to cheack with native `_contains` --> the vdom its `contains` won't work for it isn't updated yet:
childVNode && !DOCUMENT.documentElement._contains(childDomNode) && childVNode._destroy();
}
// add the new childNodes:
len = addedChildNodes.length;
for (i=0; i<len; i++) {
childDomNode = addedChildNodes[i];
// find its index in the true DOM:
index = node.childNodes.indexOf(childDomNode);
// create the vnode:
vchildnode = domNodeToVNode(childDomNode);
//======================================================================================================
// TODO: remove this block of code: we shouldn;t be needing it
// that is: when the alert never rises (which I expect it doesn't)
// prevent double definitions (for whatever reason):
// check if there is a vChild with the same domNode and remove it:
var vChildNodes = vnode.vChildNodes;
var len2 = vChildNodes ? vChildNodes.length : 0;
var j;
for (j=0; j<len2; j++) {
var checkChildVNode = vChildNodes[j];
if (checkChildVNode.domNode===node) {
checkChildVNode._destroy();
alert('double deleted');
break;
}
}
// END OF removable block
//======================================================================================================
// add the vnode:
vchildnode._moveToParent(vnode, index);
}
}
}
});
})).observe(DOCUMENT, observerConfig);
};
setupObserver();
};
//--- definition API of unmodified `Element`-methods ------
/**
* Returns the specified attribute of the specified element, as an Attr node.
*
* @method getAttributeNode
* @return {attributeNode}
*/
/**
* Returns a text rectangle object that encloses a group of text rectangles. The returned value is
* a TextRectangle object which is the union of the rectangles returned by getClientRects() for the element,
* i.e., the CSS border-boxes associated with the element.
*
* The returned value is a TextRectangle object, which contains read-only left, top, right and bottom properties
* describing the border-box in pixels. top and left are relative to the top-left of the viewport.
*
* @method getBoundingClientRect
* @return {attributeNode} Therectangle object that encloses a group of text rectangles.
*/
/**
* Returns a collection of rectangles that indicate the bounding rectangles for each box in a client.
*
* The returned value is a collection of ClientRect objects, one for each CSS border box associated with the element.
* Each ClientRect object contains read-only left, top, right and bottom properties describing the border box, in pixels,
* with the top-left relative to the top-left of the viewport. For tables with captions,
* the caption is included even though it's outside the border box of the table.
*
* @method getClientRects
* @return {Collection}
*/
/**
* Returns a new NodeIterator object with this Element as root.
*
* The NodeIterator is a snapshot of the dom at the time this method was called. It is not updated when changes of the dom are made afterwards.
*
* @method createNodeIterator
* @param [whatToShow] {Number} Filter specification constants from the NodeFilter DOM interface, indicating which nodes to iterate over.
* You can use or sum one of the next properties:
* <ul>
* <li>window.NodeFilter.SHOW_ELEMENT</li>
* <li>window.NodeFilter.SHOW_COMMENT</li>
* <li>window.NodeFilter.SHOW_TEXT</li>
* </ul>
* @param [filter] {NodeFilter|function} An object implementing the NodeFilter interface or a function. See https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter
* @return {NodeIterator}
* @since 0.0.1
*/
/**
* Returns an HTMLCollection of all Elements within this Element, that match their classes with the supplied `classNames` argument.
* To match multiple different classes, separate them with a `comma`.
*
* getElementsByClassName is life presentation of the dom. The returned HTMLCollection gets updated when the dom changes.
*
* NOTE: it is highly recomended to use `document.getAll` because that method takes advantage of the vdom.
*
* @method getElementsByClassName
* @param classNames {String} the classes to search for
* @return {HTMLCollection} life Array with Elements
*/
/**
* Returns an HTMLCollection of all Elements within this Element, that match their `name`-attribute with the supplied `name` argument.
*
* getElementsByName is life presentation of the dom. The returned HTMLCollection gets updated when the dom changes.
*
* NOTE: it is highly recomended to use `document.getAll` because that method takes advantage of the vdom.
*
* @method getElementsByName
* @param name {String} the property of name-attribute to search for
* @return {HTMLCollection} life Array with Elements
*/
/**
* Returns an HTMLCollection of all Elements within this Element, that match their `name`-attribute with the supplied `name` argument.
*
* getElementsByTagName is life presentation of the dom. The returned HTMLCollection gets updated when the dom changes.
*
* NOTE: it is highly recomended to use `document.getAll` because that method takes advantage of the vdom.
*
* @method getElementsByTagName
* @param tagNames {String} the tags to search for
* @return {HTMLCollection} life Array with Elements
*/
/**
* Parses the specified text as HTML and inserts the resulting nodes into the DOM tree at a specified position.
*
* @method insertAdjacentHTML
* @param position {String}
* <ul>
* <li>'beforebegin' Before the element itself</li>
* <li>'afterbegin' Just inside the element, before its first child</li>
* <li>'beforeend' Just inside the element, after its last child</li>
* <li>'afterend' After the element itself</li>
* <ul>
* @param element {Element}
*/
/**
* Removes the attribute specified by an attributeNode from the Element.
*
* @method removeAttributeNode
* @param attributeNode {attributeNode}
* @since 0.0.1
*/
/**
* Scrolls the element into view.
*
* @method scrollIntoView
*/
/**
* Sets the attribute on the Element specified by `attributeNode`
*
* @method setAttributeNode
* @param attributeNode {attributeNode}
*/
//------ events --------
/**
* Fired when a static `script` element finishes executing its script. Does not fire if the element is added dynamically, eg with appendChild().
*
* @event afterscriptexecute
*/
/**
* Fired when the code in a `script` element declared in an HTML document is about to start executing. Does not fire if the element is added dynamically, eg with appendChild().
*
* @event beforescriptexecute
*/
//------- properties --------
/**
* sets or returns an accesskey for an element. An accesskey specifies a shortcut key to activate/focus an element.
* Note: The way of accessing the shortcut key is varying in different browsers: http://www.w3schools.com/jsref/prop_html_accesskey.asp
*
* @property accessKey
* @type String
*/
/**
* Returns a live collection of all attribute nodes registered to the specified node.
* It is a NamedNodeMap, not an Array, so it has no Array methods and the Attr nodes' indexes may differ among browsers.
* To be more specific, attributes is a key/value pair of strings that represents any information regarding that attribute.
*
* Prefer to use `getAttrs()` which is much quicker, but doesn't return a life-list.
*
* @property attributes
* @type NamedNodeMap
*/
/**
* The absolute base URL of a node.
*
* @property baseURI
* @type String
* @readOnly
*/
/**
* Returns the number of children (child Elements)
*
* @property childElementCount
* @type Number
* @readOnly
*/
/**
* Returns a live collection of childNodes of the given element, either Element, TextNode or CommentNode
*
* @property childNodes
* @type NodeList
* @readOnly
*/
/**
* Returns a live collection of child Element's of the given element.
*
* @property children
* @type NodeList
* @readOnly
*/
/**
* Gets and sets the value of the class attribute of the specified element.
*
* @property className
* @type String
*/
/**
* Returns the inner height of an element in pixels, including padding but not the horizontal scrollbar height, border, or margin.
*
* @property clientHeight
* @type Number
* @readOnly
*/
/**
* The width of the left border of an element in pixels. It includes the width of the vertical scrollbar if the text direction of the element is right–to–left
* and if there is an overflow causing a left vertical scrollbar to be rendered. clientLeft does not include the left margin or the left padding.
*
* @property clientLeft
* @type Number
* @readOnly
*/
/**
* The width of the top border of an element in pixels. It does not include the top margin or padding.
*
* @property clientTop
* @type Number
* @readOnly
*/
/**
* Returns the inner width of an element in pixels, including padding but not the vertical scrollbar height, border, or margin.
*
* @property clientWidth
* @type Number
* @readOnly
*/
/**
* Reference to the first childNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* Better work with Elements only: use `firstElementChild` instead, which returns the first Element-child.
*
* @property firstChild
* @type Node
* @readOnly
* @deprecated
*/
/**
* Reference to the first Element-child, which is an Element (nodeType===1).
*
* @property firstElementChild
* @type Element
* @readOnly
*/
/**
* Gets or sets the element's attribute `href`. Only applies for the `a`-element.
*
* @property href
* @type String
*/
/**
* Gets or sets the element's identifier (attribute id).
*
* @property id
* @type String
*/
/**
* Reference to the last childNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* Better use `lastElementChild` instead, which returns the last Element-child.
*
* @property lastChild
* @type Node
* @readOnly
* @deprecated
*/
/**
* Reference to the last Element-child, where the related dom-node is an Element (nodeType===1).
*
* @property lastElementChild
* @type Element
* @readOnly
*/
/**
* Gets or sets the `name` property of a Element; it only applies to the following elements:
* `a`, `applet`, `button`, `form`, `frame`, `iframe`, `img`, `input`, `map`, `meta`, `object`, `param`, `select`, and `textarea`.
*
* @property name
* @type String
*/
/**
* Returns the Element immediately following the specified one in its parent's childNodes list, or null if the specified node is the last node in that list.
* Is an Element (nodeType===1).
*
* @property nextElementSibling
* @type Element
* @readOnly
*/
/**
* Returns the Element immediately following the specified one in its parent's childNodes list, or null if the specified node is the last node in that list.
* Is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* Do not use this, but use `lastElementChild` instead, which returns the next Element-child.
*
* @property nextElementSibling
* @type Node
* @deprecated
* @readOnly
*/
/**
* Elements tag-name
*
* @property nodeName
* @type String
* @readOnly
*/
/**
* Elements nodetype: 1==Element, 3==TextNode, 8===CommentNode
*
* @property nodeType
* @type String
* @readOnly
*/
/**
* Value/text for non-Element Nodes
*
* @property nodeValue
* @type String
* @since 0.0.1
*/
/**
* The exact width of the Element on the screen.
* Included borders and padding (no margin).
*
* Returns a number without unity.
*
* Better use `width` --> it's an alias, but has a setter as well
*
* @property offsetWidth
* @type Number
* @readOnly
* @since 0.0.1
*/
/**
* The exact height of the Element on the screen.
* Included borders and padding (no margin).
*
* Returns a number without unity.
*
* Better use `height` --> it's an alias, but has a setter as well
*
* @property offsetHeight
* @type Number
* @since 0.0.1
*/
/**
* Returns the Element's parent Element.
*
* Same as `parentNode`
*
* @property parentElement
* @type Element
*/
/**
* Returns the Element's parent Element.
*
* Same as `parentElement`
*
* @property parentNode
* @type Element
*/
/**
* Returns the Element immediately preceding the specified one in its parent's childNodes list, or null if the specified node is the last node in that list.
* Is an Element (nodeType===1).
*
* @property previousElementSibling
* @type Element
* @readOnly
*/
/**
* Returns the Element immediately preceding the specified one in its parent's childNodes list, or null if the specified node is the last node in that list.
* Is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* Do not use this, but use `previousElementSibling` instead, which returns the previous Element-child.
*
* @property previousSibling
* @deprecated
* @type Node
* @readOnly
*/
/**
* A measurement of the height of an element's content, including content not visible on the screen due to overflow.
* The scrollHeight value is equal to the minimum clientHeight the element would require in order to fit all the content in the viewpoint
* without using a vertical scrollbar. It includes the element padding but not its margin.
*
* Returns a number without unity.
*
* @property scrollHeight
* @type Number
* @readOnly
*/
/**
* Gets or sets the number of pixels that an element's content is scrolled to the left.
*
* @property scrollLeft
* @type Number
*/
/**
* Gets or sets the number of pixels that the content of an element is scrolled upward. An element's scrollTop is a measurement
* of the distance of an element's top to its topmost visible content. When an element content does not generate a vertical scrollbar,
* then its scrollTop value defaults to 0.
*
* @property scrollTop
* @type Number
*/
/**
* Returns either the width in pixels of the content of an element or the width of the element itself, whichever is greater.
* If the element is wider than its content area (for example, if there are scroll bars for scrolling through the content),
* the scrollWidth is larger than the clientWidth.
*
* Returns a number without unity.
*
* @property scrollWidth
* @type Number
* @readOnly
*/
/**
* Gets or sets the element's attribute `type`. Only applies for the `script`, `img` and `style`-elements.
*
* @property src
* @type String
*/
/**
* Gets or sets the element's attribute `style`.
*
* @property style
* @type String
*/
/**
* Gets or sets the element's attribute `type`. Only applies for the `input`-element.
*
* @property type
* @type String
*/
/**
* Gets or sets the value of an input or select Element.
*
* Note it is highly preferable to use getValue() and setValue().
*
* @property value
* @type String
* @since 0.0.1
*/
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"../css/element.css":48,"./attribute-extractor.js":92,"./element-array.js":93,"./html-parser.js":96,"./node-parser.js":97,"./vdom-ns.js":98,"./vnode.js":99,"js-ext/extra/hashmap.js":50,"js-ext/lib/object.js":53,"js-ext/lib/promise.js":54,"js-ext/lib/string.js":55,"polyfill":72,"polyfill/extra/transition.js":67,"polyfill/extra/transitionend.js":68,"polyfill/extra/vendorCSS.js":69,"utils":73,"window-ext":79}],96:[function(require,module,exports){
"use strict";
/**
* Exports `htmlToVNodes` which transforms html-text into vnodes.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* <br>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module vdom
* @submodule html-parser
* @since 0.0.1
*/
require('polyfill');
require('js-ext/lib/object.js');
var createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.HtmlParser) {
return window._ITSAmodules.HtmlParser; // HtmlParser was already created
}
var NS = require('./vdom-ns.js')(window),
extractor = require('./attribute-extractor.js')(window),
DOCUMENT = window.document,
xmlNS = NS.xmlNS,
voidElements = NS.voidElements,
nonVoidElements = NS.nonVoidElements,
TAG_OR_ATTR_START_CHARACTERS = createHashMap({
a: true,
b: true,
c: true,
d: true,
e: true,
f: true,
g: true,
h: true,
i: true,
j: true,
k: true,
l: true,
m: true,
n: true,
o: true,
p: true,
q: true,
r: true,
s: true,
t: true,
u: true,
v: true,
w: true,
x: true,
y: true,
z: true,
A: true,
B: true,
C: true,
D: true,
E: true,
F: true,
G: true,
H: true,
I: true,
J: true,
K: true,
L: true,
M: true,
N: true,
O: true,
P: true,
Q: true,
R: true,
S: true,
T: true,
U: true,
V: true,
W: true,
X: true,
Y: true,
Z: true
}),
STARTTAG_OR_ATTR_VALUE_ENDS_CHARACTERS = createHashMap({
' ': true,
'>': true
}),
ATTRUBUTE_NAME_ENDS_CHARACTER = createHashMap({
' ': true,
'=': true,
'>': true
}),
/**
* Transforms html-text into a vnodes-Array.
*
* @method htmlToVNodes
* @param htmlString {String} plain html as string
* @return {Array} array with `vnodes`
* @since 0.0.1
*/
htmlToVNodes = window._ITSAmodules.HtmlParser = function(htmlString, vNodeProto, nameSpace, parentVNode, suppressItagRender, allowScripts) {
var i = 0,
vnodes = [],
insideTagDefinition, insideComment, innerText, endTagCount, stringMarker, attributeisString, attribute, attributeValue, nestedComments,
len, j, character, character2, vnode, tag, isBeginTag, isEndTag, scriptVNode, extractClass, extractStyle, tagdefinition, is;
htmlString || (htmlString='');
len = htmlString.length;
while (i<len) {
character = htmlString[i];
character2 = htmlString[i+1];
if (insideTagDefinition) {
vnode.attrs = {};
if (character!=='>') {
// fill attributes until tagdefinition is over:
// NOTE: we need to DOUBLE check for "(character!=='>')" because the loop might set the position to '>' where an i++ would miss it!
while ((character!=='>') && (++i<len) && (character=htmlString[i]) && (character!=='>')) {
// when starting to read an attribute, finish reading until it is completely ready.
// this is, because attributes can have a '>' which shouldn't be noticed as an end-of-tag definition
if (TAG_OR_ATTR_START_CHARACTERS[character]) {
attribute = character;
while ((++i<len) && (character=htmlString[i]) && !ATTRUBUTE_NAME_ENDS_CHARACTER[character]) {
attribute += character;
}
if (character==='=') {
stringMarker = htmlString[i+1];
attributeisString = (stringMarker==='"') || (stringMarker==="'");
attributeValue = '';
if (attributeisString) {
i++;
while ((++i<len) && (character=htmlString[i]) && ((character!==stringMarker) || (htmlString[i-1]==='\\'))) {
((htmlString[i+1]!==stringMarker) || (character!=='\\')) && (attributeValue+=character);
}
}
else {
while ((++i<len) && (character=htmlString[i]) && !STARTTAG_OR_ATTR_VALUE_ENDS_CHARACTERS[character]) {
attributeValue += character;
}
// need to set the position one step behind --> the attributeloop will increase it and would otherwise miss a character
i--;
}
}
else {
attributeValue = "";
}
// always store the `is` attribute in lowercase:
(attribute.length===2) && (attribute.toLowerCase()==='is') && (attribute='is');
vnode.attrs[attribute] = attributeValue;
}
}
vnode.id = vnode.attrs.id;
extractClass = extractor.extractClass(vnode.attrs['class']);
extractClass.attrClass && (vnode.attrs['class']=extractClass.attrClass);
vnode.classNames = extractClass.classNames;
extractStyle = extractor.extractStyle(vnode.attrs.style);
extractStyle.attrStyle && (vnode.attrs.style=extractStyle.attrStyle);
vnode.styles = extractStyle.styles;
(vnode.attrs.is==='system-node') && (vnode._systemNode=true);
}
if (!vnode.isVoid) {
innerText = '';
endTagCount = 1;
// fill innerText until end-tagdefinition:
while ((endTagCount>0) && (++i<len) && (character=htmlString[i])) {
if (character==='<') {
if ((character2=htmlString[i+1]) && (character2==='/')) {
// possible end-tag
j = i+1;
isEndTag = true;
while (isEndTag && (++j<len) && (htmlString[j]!=='>')) {
if (htmlString[j].toUpperCase()!==tag[j-i-2]) {
isEndTag = false;
}
}
isEndTag && (endTagCount--);
}
else {
// possible begin-tag of the same tag (an innertag with the same tagname)
j = i;
isBeginTag = true;
while (isBeginTag && (++j<len) && (character2=htmlString[j]) && (character2!=='>') && (character2!==' ')) {
if (htmlString[j].toUpperCase()!==tag[j-i-1]) {
isBeginTag = false;
}
}
isBeginTag && (endTagCount++);
}
}
if (endTagCount>0) {
innerText += character;
}
}
(endTagCount===0) && (i=i+tag.length+3);
// in case of 'SCRIPT' or 'STYLE' tags --> just use the innertext, all other tags need to be extracted
if (NS.SCRIPT_OR_STYLE_TAG[vnode.tag]) {
// CREATE INNER TEXTNODE
scriptVNode = Object.create(vNodeProto);
scriptVNode.ns = nameSpace;
scriptVNode.nodeType = 3;
scriptVNode.domNode = DOCUMENT.createTextNode(innerText);
// create circular reference:
scriptVNode.domNode._vnode = scriptVNode;
scriptVNode.text = innerText;
scriptVNode.vParent = vnode;
vnode.vChildNodes = [scriptVNode];
}
else {
vnode.vChildNodes = (innerText!=='') ? htmlToVNodes(innerText, vNodeProto, vnode.ns, vnode, suppressItagRender, allowScripts) : [];
}
}
else {
i++; // compensate for the '>'
}
// just to be sure there won't be a `script`-tag passed inside the argument (something modern browsers never let happen):
(tag==='SCRIPT') && (tag==='XSCRIPT');
// the string-parser expects </xscript> for `script`-tags
if ((tag==='XSCRIPT') && allowScripts) {
tagdefinition = 'script';
vnode.tag = 'SCRIPT';
}
else {
tagdefinition = tag.toLowerCase();
//vnode.domNode can only be set after inspecting the attributes --> there might be an `is` attribute
if (vnode.isItag && (is=vnode.attrs.is) && !is.contains('-')) {
tagdefinition = tag + '#' + is;
}
}
vnode.domNode = vnode.ns ? DOCUMENT.createElementNS(vnode.ns, tagdefinition) : DOCUMENT.createElement(tagdefinition, suppressItagRender, allowScripts);
// create circular reference:
vnode.domNode._vnode = vnode;
vnodes[vnodes.length] = vnode;
// reset vnode to force create a new one
vnode = null;
insideTagDefinition = false;
}
else if (insideComment) {
if (character+character2+htmlString[i+2]+htmlString[i+3]==='<!--') {
nestedComments++;
}
if (character+character2+htmlString[i+2]==='-->') {
// should we close the vnode?
nestedComments--;
if (nestedComments<0) {
// yes close the commentnode
// move index to last character of comment
i = i+2;
vnode.domNode = DOCUMENT.createComment('');
// create circular reference:
vnode.domNode._vnode = vnode;
vnodes[vnodes.length] = vnode;
// reset vnode to force create a new one
vnode = null;
insideComment = false;
}
else {
vnode.text += character;
}
}
else {
vnode.text += character;
}
i++;
}
else {
// inside TextNode which could go over into an Element or CommentNode
if ((character==='<') && TAG_OR_ATTR_START_CHARACTERS[character2] && (htmlString.lastIndexOf('>')>i)) {
// begin of opening Element
// first: store current vnode:
if (vnode) {
vnode.domNode = DOCUMENT.createTextNode('');
// create circular reference:
vnode.domNode._vnode = vnode;
vnodes[vnodes.length] = vnode;
}
vnode = Object.create(vNodeProto);
vnode.ns = nameSpace;
vnode.nodeType = 1;
vnode.vParent = parentVNode;
vnode.tag = '';
vnode.classNames ={};
// find tagname:
while ((++i<len) && (character=htmlString[i]) && (!STARTTAG_OR_ATTR_VALUE_ENDS_CHARACTERS[character])) {
vnode.tag += character.toUpperCase();
}
tag = vnode.tag;
vnode.isItag = ((tag[0]==='I') && (tag[1]==='-'));
vnode.ns = xmlNS[tag] || nameSpace;
//vnode.domNode can only be set after inspecting the attributes --> there might be an `is` attribute
// check if it is a void-tag, but only need to do the regexp once per tag-element:
if (voidElements[tag]) {
vnode.isVoid = true;
}
else if (nonVoidElements[tag]) {
vnode.isVoid = false;
}
else {
vnode.isVoid = vnode.isItag ? false : !(new RegExp('</'+tag+'>', 'i')).test(htmlString);
vnode.isVoid ? (voidElements[tag]=true) : (nonVoidElements[tag]=true);
}
insideTagDefinition = true;
}
else if (character+character2+htmlString[i+2]+htmlString[i+3]==='<!--') {
// begin of CommentNode
if (vnode) {
vnode.domNode = DOCUMENT.createTextNode('');
// create circular reference:
vnode.domNode._vnode = vnode;
vnodes[vnodes.length] = vnode;
}
vnode = Object.create(vNodeProto);
vnode.ns = nameSpace;
vnode.nodeType = 8;
vnode.text = '';
vnode.vParent = parentVNode;
// move index to first character of comment
i = i+4;
insideComment = true;
nestedComments = 0;
}
else {
if (!vnode) {
// no current vnode --> create a TextNode:
vnode = Object.create(vNodeProto);
vnode.ns = nameSpace;
vnode.nodeType = 3;
vnode.text = '';
vnode.vParent = parentVNode;
}
vnode.text += character;
i++;
}
}
}
if (vnode) {
vnode.domNode = DOCUMENT.createTextNode('');
// create circular reference:
vnode.domNode._vnode = vnode;
vnodes[vnodes.length] = vnode;
}
return vnodes;
};
return htmlToVNodes;
};
},{"./attribute-extractor.js":92,"./vdom-ns.js":98,"js-ext/extra/hashmap.js":50,"js-ext/lib/object.js":53,"polyfill":72}],97:[function(require,module,exports){
"use strict";
/**
* Exports `domNodeToVNode` which transforms dom-nodes into vnodes.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i><br>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module vdom
* @submodule node-parser
* @since 0.0.1
*/
require('polyfill');
require('js-ext/lib/object.js');
var createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.NodeParser) {
return window._ITSAmodules.NodeParser; // NodeParser was already created
}
var NS = require('./vdom-ns.js')(window),
escapeEntities = NS.EscapeEntities,
extractor = require('./attribute-extractor.js')(window),
xmlNS = NS.xmlNS,
voidElements = NS.voidElements,
nonVoidElements = NS.nonVoidElements,
vNodeProto = require('./vnode.js')(window),
/**
* Transforms a dom-node into a vnode.
*
* @method domNodeToVNode
* @param domNode {Node} The dom-node to be transformed
* @param [parentVNode] {vnode} the parent-vnode that belongs to the dom-node
* @return {vnode} the vnode-representation of the dom-node
* @since 0.0.1
*/
domNodeToVNode = window._ITSAmodules.NodeParser = function(domNode, parentVNode) {
var nodeType = domNode.nodeType,
vnode, attributes, attr, i, len, childNodes, domChildNode, vChildNodes, tag,
childVNode, extractClass, extractStyle, attributeName, parentNode;
if (!NS.VALID_NODE_TYPES[nodeType]) {
// only process ElementNodes, TextNodes and CommentNodes
return;
}
vnode = Object.create(vNodeProto);
// set properties:
vnode.domNode = domNode;
// create circular reference:
vnode.domNode._vnode = vnode;
parentVNode && (vnode.ns=parentVNode.ns);
vnode.nodeType = nodeType;
vnode.vParent = parentVNode;
if (nodeType===1) {
// ElementNode
tag = vnode.tag = domNode.nodeName; // is always uppercase
vnode.isItag = ((tag[0]==='I') && (tag[1]==='-'));
vnode.ns = xmlNS[tag] || vnode.ns;
vnode.attrs = {};
attributes = domNode.attributes;
len = attributes.length;
for (i=0; i<len; i++) {
attr = attributes[i];
// always store the `is` attribute in lowercase:
attributeName = ((attr.name.length===2) && (attr.name.toLowerCase()==='is')) ? 'is' : attr.name;
vnode.attrs[attributeName] = String(attr.value);
}
vnode.id = vnode.attrs.id;
extractClass = extractor.extractClass(vnode.attrs['class']);
extractClass.attrClass && (vnode.attrs['class']=extractClass.attrClass);
vnode.classNames = extractClass.classNames;
extractStyle = extractor.extractStyle(vnode.attrs.style);
extractStyle.attrStyle && (vnode.attrs.style=extractStyle.attrStyle);
vnode.styles = extractStyle.styles;
(vnode.attrs.is==='system-node') && (vnode._systemNode=true);
if (voidElements[tag]) {
vnode.isVoid = true;
}
else if (nonVoidElements[tag]) {
vnode.isVoid = false;
}
else {
(vnode.isVoid=!(new RegExp('</'+tag+'>$', 'i')).test(domNode.outerHTML)) ? (voidElements[tag]=true) : (nonVoidElements[tag]=true);
}
if (!vnode.isVoid) {
vChildNodes = vnode.vChildNodes = [];
childNodes = domNode.childNodes;
len = childNodes.length;
for (i=0; i<len; i++) {
domChildNode = childNodes[i];
childVNode = domNodeToVNode(domChildNode, vnode);
vChildNodes[vChildNodes.length] = childVNode;
}
if (tag==='SCRIPT') {
// we register its content to its vParent and will remove it from the dom
// asyncrouniously after the dom is parsed
if (!parentVNode) {
// try to look in the dom for its parent
parentNode = domNode.parentNode;
parentVNode = parentNode && parentNode.vnode;
}
if (parentVNode) {
if (len>0) {
parentVNode._scripts || (parentVNode._scripts=[]);
parentVNode._scripts[parentVNode._scripts.length] = vChildNodes[0].text;
}
}
}
}
}
else {
// TextNode or CommentNode
vnode.text = escapeEntities(domNode.nodeValue);
// vnode.text = (nodeType===3) ? escapeEntities(domNode.nodeValue) : domNode.nodeValue;
}
// store vnode's id:
vnode.storeId();
return vnode;
};
return domNodeToVNode;
};
},{"./attribute-extractor.js":92,"./vdom-ns.js":98,"./vnode.js":99,"js-ext/extra/hashmap.js":50,"js-ext/lib/object.js":53,"polyfill":72}],98:[function(require,module,exports){
/**
* Creates a Namespace that can be used accros multiple vdom-modules to share information.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* <br>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
*
* @module vdom
* @submodule vdom-ns
* @class NS-vdom
* @since 0.0.1
*/
"use strict";
require('js-ext/lib/object.js');
require('polyfill');
var createHashMap = require('js-ext/extra/hashmap.js').createMap,
// for escaping entoties inside domNode.nodeValue, we need this trick: https://code.google.com/p/jslibs/wiki/JavascriptTips
/*jshint proto:true */
ENTITY_TO_CODE = { __proto__: null,
apos:0x0027,quot:0x0022,amp:0x0026,lt:0x003C,gt:0x003E,nbsp:0x00A0,iexcl:0x00A1,cent:0x00A2,pound:0x00A3,
curren:0x00A4,yen:0x00A5,brvbar:0x00A6,sect:0x00A7,uml:0x00A8,copy:0x00A9,ordf:0x00AA,laquo:0x00AB,
not:0x00AC,shy:0x00AD,reg:0x00AE,macr:0x00AF,deg:0x00B0,plusmn:0x00B1,sup2:0x00B2,sup3:0x00B3,
acute:0x00B4,micro:0x00B5,para:0x00B6,middot:0x00B7,cedil:0x00B8,sup1:0x00B9,ordm:0x00BA,raquo:0x00BB,
frac14:0x00BC,frac12:0x00BD,frac34:0x00BE,iquest:0x00BF,Agrave:0x00C0,Aacute:0x00C1,Acirc:0x00C2,Atilde:0x00C3,
Auml:0x00C4,Aring:0x00C5,AElig:0x00C6,Ccedil:0x00C7,Egrave:0x00C8,Eacute:0x00C9,Ecirc:0x00CA,Euml:0x00CB,
Igrave:0x00CC,Iacute:0x00CD,Icirc:0x00CE,Iuml:0x00CF,ETH:0x00D0,Ntilde:0x00D1,Ograve:0x00D2,Oacute:0x00D3,
Ocirc:0x00D4,Otilde:0x00D5,Ouml:0x00D6,times:0x00D7,Oslash:0x00D8,Ugrave:0x00D9,Uacute:0x00DA,Ucirc:0x00DB,
Uuml:0x00DC,Yacute:0x00DD,THORN:0x00DE,szlig:0x00DF,agrave:0x00E0,aacute:0x00E1,acirc:0x00E2,atilde:0x00E3,
auml:0x00E4,aring:0x00E5,aelig:0x00E6,ccedil:0x00E7,egrave:0x00E8,eacute:0x00E9,ecirc:0x00EA,euml:0x00EB,
igrave:0x00EC,iacute:0x00ED,icirc:0x00EE,iuml:0x00EF,eth:0x00F0,ntilde:0x00F1,ograve:0x00F2,oacute:0x00F3,
ocirc:0x00F4,otilde:0x00F5,ouml:0x00F6,divide:0x00F7,oslash:0x00F8,ugrave:0x00F9,uacute:0x00FA,ucirc:0x00FB,
uuml:0x00FC,yacute:0x00FD,thorn:0x00FE,yuml:0x00FF,OElig:0x0152,oelig:0x0153,Scaron:0x0160,scaron:0x0161,
Yuml:0x0178,fnof:0x0192,circ:0x02C6,tilde:0x02DC,Alpha:0x0391,Beta:0x0392,Gamma:0x0393,Delta:0x0394,
Epsilon:0x0395,Zeta:0x0396,Eta:0x0397,Theta:0x0398,Iota:0x0399,Kappa:0x039A,Lambda:0x039B,Mu:0x039C,
Nu:0x039D,Xi:0x039E,Omicron:0x039F,Pi:0x03A0,Rho:0x03A1,Sigma:0x03A3,Tau:0x03A4,Upsilon:0x03A5,
Phi:0x03A6,Chi:0x03A7,Psi:0x03A8,Omega:0x03A9,alpha:0x03B1,beta:0x03B2,gamma:0x03B3,delta:0x03B4,
epsilon:0x03B5,zeta:0x03B6,eta:0x03B7,theta:0x03B8,iota:0x03B9,kappa:0x03BA,lambda:0x03BB,mu:0x03BC,
nu:0x03BD,xi:0x03BE,omicron:0x03BF,pi:0x03C0,rho:0x03C1,sigmaf:0x03C2,sigma:0x03C3,tau:0x03C4,
upsilon:0x03C5,phi:0x03C6,chi:0x03C7,psi:0x03C8,omega:0x03C9,thetasym:0x03D1,upsih:0x03D2,piv:0x03D6,
ensp:0x2002,emsp:0x2003,thinsp:0x2009,zwnj:0x200C,zwj:0x200D,lrm:0x200E,rlm:0x200F,ndash:0x2013,
mdash:0x2014,lsquo:0x2018,rsquo:0x2019,sbquo:0x201A,ldquo:0x201C,rdquo:0x201D,bdquo:0x201E,dagger:0x2020,
Dagger:0x2021,bull:0x2022,hellip:0x2026,permil:0x2030,prime:0x2032,Prime:0x2033,lsaquo:0x2039,rsaquo:0x203A,
oline:0x203E,frasl:0x2044,euro:0x20AC,image:0x2111,weierp:0x2118,real:0x211C,trade:0x2122,alefsym:0x2135,
larr:0x2190,uarr:0x2191,rarr:0x2192,darr:0x2193,harr:0x2194,crarr:0x21B5,lArr:0x21D0,uArr:0x21D1,
rArr:0x21D2,dArr:0x21D3,hArr:0x21D4,forall:0x2200,part:0x2202,exist:0x2203,empty:0x2205,nabla:0x2207,
isin:0x2208,notin:0x2209,ni:0x220B,prod:0x220F,sum:0x2211,minus:0x2212,lowast:0x2217,radic:0x221A,
prop:0x221D,infin:0x221E,ang:0x2220,and:0x2227,or:0x2228,cap:0x2229,cup:0x222A,int:0x222B,
there4:0x2234,sim:0x223C,cong:0x2245,asymp:0x2248,ne:0x2260,equiv:0x2261,le:0x2264,ge:0x2265,
sub:0x2282,sup:0x2283,nsub:0x2284,sube:0x2286,supe:0x2287,oplus:0x2295,otimes:0x2297,perp:0x22A5,
sdot:0x22C5,lceil:0x2308,rceil:0x2309,lfloor:0x230A,rfloor:0x230B,lang:0x2329,rang:0x232A,loz:0x25CA,
spades:0x2660,clubs:0x2663,hearts:0x2665,diams:0x2666
},
/*jshint proto:false */
charToEntity = {},
// Void Elements - HTML 4.01
DEFAULT_VOID = createHashMap({
AREA: true,
BASE: true,
BASEFONT: true,
BR: true,
COL: true,
FRAME: true,
HR: true,
IMG: true,
INPUT: true,
ISINDEX: true,
LINK: true,
META: true,
PARAM: true,
EMBED: true
}),
// Block Elements - HTML 4.01
DEFAULT_NON_BLOCK = createHashMap({
ADDRESS: true,
APPLET: true,
BLOCKQUITE: true,
BUTTON: true,
CENTER: true,
DD: true,
DEL: true,
DIR: true,
DIV: true,
DL: true,
DT: true,
FIELDSET: true,
FORM: true,
FRAMESET: true,
IFRAME: true,
INS: true,
ISINDEX: true,
LI: true,
MAP: true,
MENU: true,
NOFRAMES: true,
NOSCRIPT: true,
OBJECT: true,
OL: true,
P: true,
PRE: true,
SCRIPT: true,
TABLE: true,
TBODY: true,
TD: true,
TFOOT: true,
TH: true,
THEAD: true,
TR: true,
UL: true
}),
entityName;
for (entityName in ENTITY_TO_CODE) {
charToEntity[String.fromCharCode(ENTITY_TO_CODE[entityName])] = entityName;
}
module.exports = function (window) {
var NS;
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.VDOM_NS) {
return window._ITSAmodules.VDOM_NS; // VDOM_NS was already created
}
NS = window._ITSAmodules.VDOM_NS = createHashMap();
/**
* Reference to the VElement of document.body (gets its value as soon as it gets refered to)
*
* @property body
* @default null
* @type VElement
* @since 0.0.1
*/
NS.body = null;
NS.xmlNS = createHashMap({
SVG: 'http://www.w3.org/2000/svg',
XBL: 'http://www.mozilla.org/xbl',
XUL: 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul',
MATH: 'http://www.w3.org/1998/Math/MathML',
XLINK: 'http://www.w3.org/1999/xlink'
});
/**
* A hash with all node'ids (of all the domnodes that have an id). The value is a reference to an VElement.
*
* @property nodeids
* @default {}
* @type Object
* @since 0.0.1
*/
NS.nodeids || (NS.nodeids=createHashMap());
/**
* A hash with all encountered non-void Elements
*
* @property nonVoidElements
* @default {}
* @type Object
* @since 0.0.1
*/
NS.nonVoidElements || (NS.nonVoidElements=DEFAULT_NON_BLOCK);
/**
* A hash to identify what tagNames are equal to `SCRIPT` or `STYLE`.
*
* @property SCRIPT_OR_STYLE_TAG
* @default {SCRIPT: true, STYLE: true}
* @type Object
* @since 0.0.1
*/
NS.SCRIPT_OR_STYLE_TAG = createHashMap({
// the string-parser expects </xscript> for `script`-tags
XSCRIPT: true,
STYLE: true
});
/**
* A hash with all nodeTypes that should be captured by the vDOM.
*
* @property VALID_NODE_TYPES
* @default {1: true, 3: true, 8: true}
* @type Object
* @since 0.0.1
*/
NS.VALID_NODE_TYPES = createHashMap({
1: true,
3: true,
8: true
});
/**
* A hash with all encountered void Elements
*
* @property voidElements
* @default {}
* @type Object
* @since 0.0.1
*/
NS.voidElements || (NS.voidElements=DEFAULT_VOID);
NS.UnescapeEntities = function(str) {
return str.replace(
/&(.+?);/g,
function(str, ent) {
return String.fromCharCode( ent[0]!=='#' ? ENTITY_TO_CODE[ent] : (ent[1]==='x' ? parseInt(ent.substr(2),16) : parseInt(ent.substr(1)) ) );
}
);
};
NS.EscapeEntities = function(str) {
return str.replace(
/[^\x20-\x7E]/g,
function(str) {
return charToEntity[str] ? '&'+charToEntity[str]+';' : str;
}
);
};
return NS;
};
},{"js-ext/extra/hashmap.js":50,"js-ext/lib/object.js":53,"polyfill":72}],99:[function(require,module,exports){
"use strict";
/**
* Delivers the `vnode` prototype object, which is a virtualisation of an `Element` inside the Dom.
* These Elements work smoothless with the vdom (see ...).
*
* vnodes are much quicker to access and walk through than native dom-nodes. However, this is a module you don't need
* by itself: `Element`-types use these features under the hood.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* <br>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
*
* @module vdom
* @submodule vnode
* @class vnode
* @since 0.0.1
*/
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('js-ext/lib/string.js');
require('polyfill');
var createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.VNode) {
return window._ITSAmodules.VNode; // VNODE was already created
}
var NS = require('./vdom-ns.js')(window),
extractor = require('./attribute-extractor.js')(window),
DOCUMENT = window.document,
LightMap = require('js-ext/extra/lightmap.js'),
MUTATION_EVENTS = new LightMap(),
BATCH_WILL_RUN = false,
xmlNS = NS.xmlNS,
nodeids = NS.nodeids,
htmlToVNodes = require('./html-parser.js')(window),
timers = require('utils/lib/timers.js'),
async = timers.async,
later = timers.later,
/*jshint proto:true */
PROTO_SUPPORTED = !!Object.__proto__,
/*jshint proto:false */
// cleanup memory after 1 minute: removed nodes SHOULD NOT be accessed afterwards
// because vnode would be recalculated and might be different from before
DESTROY_DELAY = 60000,
unescapeEntities = NS.UnescapeEntities,
NTH_CHILD_REGEXP = /^(?:(\d*)[n|N])([\+|\-](\d+))?$/, // an+b
STRING = 'string',
CLASS = 'class',
STYLE = 'style',
ID = 'id',
NODE= 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
EV_REMOVED = NODE+REMOVE,
EV_INSERTED = NODE+INSERT,
EV_CONTENT_CHANGE = NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = ATTRIBUTE+INSERT,
SPLIT_CHARACTER = createHashMap({
' ': true,
'>': true,
'+': true, // only select the element when it is immediately preceded by the former element
'~': true // only the element when it has the former element as a sibling. (just like `+`, but less strict)
}),
STORABLE_SPLIT_CHARACTER = createHashMap({
'>': true,
'+': true,
'~': true
}),
SIBLING_MATCH_CHARACTER = createHashMap({
'+': true,
'~': true
}),
ATTR_DETAIL_SPECIFIERS = createHashMap({
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to attribute-name end-tokens.
*
* @property END_ATTRIBUTENAME
* @default {
* '=': true,
* ']': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
END_ATTRIBUTENAME = createHashMap({
'=': true,
']': true,
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to different changes of Element nodeType changes.
*
* @property NODESWITCH
* @default {
* 1: {
* 1: 1,
* 3: 2,
* 8: 3
* },
* 3: {
* 1: 4,
* 3: 5,
* 8: 6
* },
* 8: {
* 1: 7,
* 3: 8,
* 8: 9
* }
* }
* @type Object
* @protected
* @since 0.0.1
*/
NODESWITCH = createHashMap({
1: createHashMap({
1: 1, // oldNodeType==Element, newNodeType==Element
3: 2, // oldNodeType==Element, newNodeType==TextNode
8: 3 // oldNodeType==Element, newNodeType==Comment
}),
3: createHashMap({
1: 4, // oldNodeType==TextNode, newNodeType==Element
3: 5, // oldNodeType==TextNode, newNodeType==TextNode
8: 6 // oldNodeType==TextNode, newNodeType==Comment
}),
8: createHashMap({
1: 7, // oldNodeType==Comment, newNodeType==Element
3: 8, // oldNodeType==Comment, newNodeType==TextNode
8: 9 // oldNodeType==Comment, newNodeType==Comment
})
}),
/**
* Object to gain quick access to selector start-tokens.
*
* @property SELECTOR_IDENTIFIERS
* @default {
* '#': 1,
* '.': 2,
* '[': 3
* }
* @type Object
* @protected
* @since 0.0.1
*/
SELECTOR_IDENTIFIERS = createHashMap({
'#': 1,
'.': 2,
'[': 3,
':': 4
}),
PSEUDO_FIRST_CHILD = ':first-child',
PSEUDO_FIRST_OF_TYPE = ':first-of-type',
PSEUDO_LAST_CHILD = ':last-child',
PSEUDO_LAST_OF_TYPE = ':last-of-type',
PSEUDO_NTH_CHILD = ':nth-child',
PSEUDO_NTH_LAST_CHILD = ':nth-last-child',
PSEUDO_NTH_LAST_OF_TYPE = ':nth-last-of-type',
PSEUDO_NTH_OF_TYPE = ':nth-of-type',
PSEUDO_ONLY_OF_TYPE = ':only-of-type',
PSEUDO_ONLY_CHILD = ':only-child',
/**
* Object to gain quick access to the selectors that required children
*
* @property PSEUDO_REQUIRED_CHILDREN
* @default {
* ':first-child': true,
* ':first-of-type': true,
* ':last-child': true,
* ':last-of-type': true,
* ':nth-child': true,
* ':nth-last-child': true,
* ':nth-last-of-type': true,
* ':nth-of-type': true,
* ':only-of-type': true,
* ':only-child': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
PSEUDO_REQUIRED_CHILDREN = createHashMap(),
_matchesSelectorItem, _matchesOneSelector, _findElementSibling, vNodeProto, _markRemoved, _tryReplaceChild,
_splitSelector, _findNodeSibling, _matchNthChild, _batchEmit, _emitDestroyChildren, _tryRemoveDomNode;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_CHILD] = true;
/**
* Searches for the next -or previous- node-sibling (nodeType of 1, 3 or 8).
*
* @method _findNodeSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findNodeSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
index = vParent.vChildNodes.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildNodes[index];
};
/**
* Searches for the next -or previous- Element-sibling (nodeType of 1).
*
* @method _findElementSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findElementSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
if (vnode.nodeType===1) {
index = vParent.vChildren.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildren[index];
}
else {
/*jshint noempty:true */
while ((vnode=_findNodeSibling(vnode, next)) && (vnode.nodeType!==1)) {}
/*jshint noempty:false */
return vnode;
}
};
/**
* Check whether the vnode matches a "nth-child" test, which is used for css pseudoselectors like `nth-child`, `nth-of-type` etc.
*
* @method _matchNthChild
* @param pseudoArg {String} the argument for nth-child
* @param index {Number} the index of the inspected vnode
* @return {Boolean} whether the vnode matches the nthChild test
* @protected
* @private
* @since 0.0.1
*/
_matchNthChild = function(pseudoArg, index) {
var match, k, a, b, nodeOk, nthIndex, sign, isNumber;
(pseudoArg==='even') && (pseudoArg='2n');
(pseudoArg==='odd') && (pseudoArg='2n+1');
match = pseudoArg.match(NTH_CHILD_REGEXP) || (isNumber=pseudoArg.validateNumber());
if (!match) {
return false;
}
// pseudoArg follows the pattern: `an+b`
if (!isNumber) {
a = match[1];
sign = match[2];
b = match[3] || 0;
(b==='') && (b=0);
}
else {
b = pseudoArg;
}
sign && (sign=sign[0]);
if (!a) {
// only fixed index to match
return (sign==='-') ? false : (parseInt(b, 10)===index);
}
else {
// we need to iterate
nodeOk = false;
b = window.Number(b);
for (k=0; !nodeOk; k++) {
nthIndex = (sign==='-') ? (a*k) - b : (a*k) + b;
if (nthIndex===index) {
nodeOk = true;
}
else if (nthIndex>index) {
// beyond index --> will never become a fix anymore
return false;
}
}
return nodeOk;
}
};
/**
* Check whether the vnode matches the css-selector. the css-selector should be a single selector,
* not multiple, so it shouldn't contain a `comma`.
*
* @method _matchesOneSelector
* @param vnode {vnode} the vnode to inspect
* @param selector {String} the selector-item to check the match for
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches the css-selector
* @protected
* @private
* @since 0.0.1
*/
_matchesOneSelector = function(vnode, selector, relatedVNode) {
var selList = _splitSelector(selector),
size = selList.length,
selMatch = false,
i, selectorItem, last,
rightvnode, relationMatch, checkRelation;
if (STORABLE_SPLIT_CHARACTER[selList[size-1]]) {
return false;
}
relationMatch = function(leftVNode, rightVNode, relationCharacter) {
var match, vParent, vChildren;
// when `selector` starts with `>`, `~` or `+`, then
// there should also be a match comparing a related node!
switch (relationCharacter) {
case '>':
leftVNode || (leftVNode=rightVNode.vParent);
match = (leftVNode.vChildren.indexOf(rightVNode)!==-1);
break;
case '~':
leftVNode || (leftVNode=rightVNode.vFirstElement);
vParent = leftVNode.vParent;
vChildren = vParent && vParent.vChildren;
match = vParent && (vChildren.indexOf(leftVNode)<vChildren.indexOf(rightVNode));
break;
case '+':
leftVNode || (leftVNode=rightVNode.vPreviousElement);
match = (leftVNode.vNextElement === rightVNode);
}
return !!match;
};
last = size - 1;
i = last;
while (vnode && ((selMatch || (i===last)) && (i>=0))) {
selectorItem = selList[i];
if (STORABLE_SPLIT_CHARACTER[selectorItem]) {
checkRelation = selectorItem;
i--;
}
else {
selMatch = _matchesSelectorItem(vnode, selectorItem);
if (!selMatch && (i===last)) {
return false;
}
while (!selMatch && vnode && (i!==last)) {
// may also look at nodes higher in the chain
vnode = SIBLING_MATCH_CHARACTER[selList[i+1]] ? vnode.vPreviousElement : vnode.vParent;
selMatch = vnode && _matchesSelectorItem(vnode, selectorItem);
}
selMatch && checkRelation && vnode && (selMatch=relationMatch(vnode, rightvnode, checkRelation));
rightvnode = vnode;
if (vnode) {
// do a precheck on the next checkRelation:
// and determine whether to set the vnode upper the tree or at the previous sibling:
vnode = SIBLING_MATCH_CHARACTER[selList[i-1]] ? vnode.vPreviousElement : vnode.vParent;
}
if (selMatch) {
checkRelation = false;
i--;
}
}
}
if ((rightvnode===relatedVNode) || (i!==-1)) {
selMatch = false;
}
selMatch && checkRelation && rightvnode && (selMatch=relationMatch(relatedVNode, rightvnode, checkRelation));
return selMatch;
};
/**
* Check whether the vnode matches one specific selector-item. Suppose the css-selector: "#mynode li.red .blue"
* then there are 3 selector-items: "#mynode", "li.red" and ".blue"
*
* This method also can handle the new selectors:
* <ul>
* <li>[att^=val] –-> the “begins with” selector</li>
* <li>[att$=val] –-> the “ends with” selector</li>
* <li>[att*=val] –-> the “contains” selector (might be a substring)</li>
* <li>[att~=val] –-> the “contains” selector as a separate word, separated by spaces</li>
* <li>[att|=val] –-> the “contains” selector as a separate word, separated by `|`</li>
* <li>+ --> (same level)</li>
* <li>~ --> (same level)</li>
* </ul>
*
* @method _matchesSelectorItem
* @param vnode {Object} the vnode to inspect
* @param selectorItem {String} the selector-item to check the match for
* @return {Boolean} whether the vnode matches the selector-item
* @protected
* @private
* @since 0.0.1
*/
_matchesSelectorItem = function (vnode, selectorItem) {
var i = 0,
len = selectorItem.length,
character = selectorItem[0],
tagName, id, className, attributeName, attributeValue, stringMarker, attributeisString, isBoolean, insideAttributeValue, insideAttribute,
vParent, checkBoolean, treatment, k, min, max, value, len2, index, found, pseudo, pseudoArg, arglevel, count, vParentVChildren;
if (selectorItem==='*') {
return true;
}
if (!SELECTOR_IDENTIFIERS[character]) {
// starts with tagName
tagName = '';
// reposition i to continue in the right way:
i--;
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
tagName += character;
}
if (tagName.toUpperCase()!==vnode.tag) {
return false;
}
}
while (i<len) {
switch (character) {
case '#':
id = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
id += character;
}
if (id!==vnode.id) {
return false;
}
break;
case '.':
className = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
className += character;
}
if (!vnode.hasClass(className)) {
return false;
}
break;
case '[':
attributeName = '';
while ((++i<len) && (character=selectorItem[i]) && !END_ATTRIBUTENAME[character]) {
attributeName += character;
}
// if character===']' then we have an attribute without a value-definition
if (!vnode.attrs[attributeName] || ((character===']') && (vnode.attrs[attributeName]!==''))) {
return !!vnode.attrs[attributeName];
}
// now we read the value of the attribute
// however, it could be that the selector has a special `detailed` identifier set (defined by: ATTR_DETAIL_SPECIFIERS)
if (ATTR_DETAIL_SPECIFIERS[character]) {
treatment = character; // store the character to know how the attributedata should be treaded
i++; // character should be a "=" by now
}
else {
treatment = null;
}
attributeValue = '';
stringMarker = selectorItem[i+1];
attributeisString = (stringMarker==='"') || (stringMarker==="'");
attributeisString && (i++);
// end of attributaValue = (character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker))
while ((++i<len) && (character=selectorItem[i]) && !((character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker)))) {
attributeValue += character;
}
if (attributeisString) {
// if attribute is string, then we need to _remove to last stringmarker
attributeValue = attributeValue.substr(0, attributeValue.length-1);
}
else {
// if attribute is no string, then we need to typecast its value
isBoolean = ((attributeValue.length>3) && (attributeValue.length<6) &&
(checkBoolean=attributeValue.toUpperCase()) &&
((checkBoolean==='FALSE') || (checkBoolean==='TRUE')));
// typecast the value to either Boolean or Number:
attributeValue = isBoolean ? (checkBoolean==='TRUE') : parseFloat(attributeValue);
}
// depending upon how the attributedata should be treated:
if (treatment) {
switch (treatment) {
case '^': // “begins with” selector
if (!vnode.attrs[attributeName].startsWith(attributeValue)) {
return false;
}
break;
case '$': // “ends with” selector
if (!vnode.attrs[attributeName].endsWith(attributeValue)) {
return false;
}
break;
case '*': // “contains” selector (might be a substring)
if (!vnode.attrs[attributeName].contains(attributeValue)) {
return false;
}
break;
case '~': // “contains” selector as a separate word, separated by spaces
if (!(' '+vnode.attrs[attributeName]+' ').contains(' '+attributeValue+' ')) {
return false;
}
break;
case '|': // “contains” selector as a separate word, separated by `|`
if (!('|'+vnode.attrs[attributeName]+'|').contains('|'+attributeValue+'|')) {
return false;
}
break;
}
}
else if (vnode.attrs[attributeName]!==attributeValue) {
return false;
}
// we still need to increase one position:
(++i<len) && (character=selectorItem[i]);
break;
case ':':
// we have a pseudo-selector
// first, find out which one
// because '::' is a valid start (though without any selection), we start to back the next character as well:
pseudo = ':'+selectorItem[++i];
pseudoArg = '';
vParent = vnode.vParent;
vParentVChildren = vParent && vParent.vChildren;
// pseudo-selectors might have an argument passed in, like `:nth-child(2n+1)` or `:not([type="checkbox"])` --> we
// store this argument inside `pseudoArg`
// also note that combinations are possible with `:not` --> `:not(:nth-child(2n+1))`
// also note that we cannot "just" look for a closing character when running into the usage of attributes:
// for example --> `:not([data-x="some data :)"])`
// that's why -once we are inside attribute-data- we need to continue until the attribute-data ends
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
if (character==='(') {
// starting arguments
arglevel = 1;
insideAttribute = false;
insideAttributeValue = false;
while ((++i<len) && (character=selectorItem[i]) && (arglevel>0)) {
if (!insideAttribute) {
if (character==='(') {
arglevel++;
}
else if (character===')') {
arglevel--;
}
else if (character==='[') {
insideAttribute = true;
}
}
else {
// inside attribute
if (!insideAttributeValue) {
if ((character==='"') || (character==="'")) {
insideAttributeValue = true;
stringMarker = character;
}
else if (character===']') {
insideAttribute = false;
}
}
else if ((character===stringMarker) && (selectorItem[i+1]===']')) {
insideAttributeValue = false;
}
}
(arglevel>0) && (pseudoArg+=character);
}
}
else {
pseudo += character;
}
}
// now, `pseudo` is known as well as its possible pseudoArg
if (!vParentVChildren && PSEUDO_REQUIRED_CHILDREN[pseudo]) {
return false;
}
switch (pseudo) {
case ':checked': // input:checked Selects every checked <input> element
if (!vnode.attrs.checked) {
return false;
}
break;
case ':disabled': // input:disabled Selects every disabled <input> element
if (!vnode.attrs.disabled) {
return false;
}
break;
case ':empty': // p:empty Selects every <p> element that has no children (including text nodes)
if (vnode.vChildNodes && (vnode.vChildNodes.length>0)) {
return false;
}
break;
case ':enabled': // input:enabled Selects every enabled <input> element
if (vnode.attrs.disabled) {
return false;
}
break;
case PSEUDO_FIRST_CHILD: // p:first-child Selects every <p> element that is the first child of its parent
if (vParentVChildren[0]!==vnode) {
return false;
}
break;
case PSEUDO_FIRST_OF_TYPE: // p:first-of-type Selects every <p> element that is the first <p> element of its parent
for (k=vParentVChildren.indexOf(vnode)-1; k>=0; k--) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':focus': // input:focus Selects the input element which has focus
if (vnode.domNode!==DOCUMENT.activeElement) {
return false;
}
break;
case ':in-range': // input:in-range Selects input elements with a value within a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || (value<min) || (value>max)) {
return false;
}
break;
case ':lang': // p:lang(it) Selects every <p> element with a lang attribute equal to "it" (Italian)
if (vnode.attrs.lang!==pseudoArg) {
return false;
}
break;
case PSEUDO_LAST_CHILD: // p:last-child Selects every <p> element that is the last child of its parent
if (vParentVChildren[vParentVChildren.length-1]!==vnode) {
return false;
}
break;
case PSEUDO_LAST_OF_TYPE: // p:last-of-type Selects every <p> element that is the last <p> element of its parent
len2 = vParentVChildren.length;
for (k=vParentVChildren.indexOf(vnode)+1; k<len2; k++) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':not': // :not(p) Selects every element that is not a <p> element
if (vnode.matchesSelector(pseudoArg)) {
return false;
}
break;
case PSEUDO_NTH_CHILD: // p:nth-child(2) Selects every <p> element that is the second child of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
index = vParentVChildren.indexOf(vnode)+1;
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_CHILD: // p:nth-last-child(2) Selects every <p> element that is the second child of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_OF_TYPE: // p:nth-last-of-type(2) Selects every <p> element that is the second <p> element of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
index = 0;
for (k=vParentVChildren.length-1; (k>=0) && !found; k--) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_OF_TYPE: // p:nth-of-type(2) Selects every <p> element that is the second <p> element of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
len2 = vParentVChildren.length;
index = 0;
for (k=0; (k<len2) && !found; k++) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_ONLY_OF_TYPE: // p:only-of-type Selects every <p> element that is the only <p> element of its parent
len2 = vParentVChildren.length;
count = 0;
for (k=0; (k<len2) && (count<=1); k++) {
(vParentVChildren[k].tag===vnode.tag) && count++;
}
if (count!==1) {
return false;
}
break;
case PSEUDO_ONLY_CHILD: // p:only-child Selects every <p> element that is the only child of its parent
if (vParentVChildren.length!==1) {
return false;
}
break;
case ':optional': // input:optional Selects input elements with no "required" attribute
if (vnode.attrs.required) {
return false;
}
break;
case ':out-of-range': // input:out-of-range Selects input elements with a value outside a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || ((value>=min) && (value<=max))) {
return false;
}
break;
case ':read-only': // input:read-only Selects input elements with the "readonly" attribute specified
if (!vnode.attrs.readonly) {
return false;
}
break;
case ':read-write': // input:read-write Selects input elements with the "readonly" attribute NOT specified
if (vnode.attrs.readonly) {
return false;
}
break;
case ':required': // input:required Selects input elements with the "required" attribute specified
if (!vnode.attrs.required) {
return false;
}
break;
case ':root': // Selects the document's root element
if (vnode.domNode!==DOCUMENT.documentElement) {
return false;
}
break;
}
}
}
return true;
};
/**
* Splits the selector into separate subselector-items that should match different elements through the tree.
* Special characters '>' and '+' are added as separate items in the hash.
*
* @method _splitSelector
* @param selector {String} the selector-item to check the match for
* @return {Array} splitted selectors
* @protected
* @private
* @since 0.0.1
*/
_splitSelector = function(selector) {
var list = [],
len = selector.length,
sel = '',
i, character, insideDataAttr;
for (i=0; i<len; i++) {
character = selector[i];
if (character==='[') {
sel += character;
insideDataAttr = true;
}
else if (character===']') {
sel += character;
insideDataAttr = false;
}
else if (insideDataAttr || !SPLIT_CHARACTER[character]) {
sel += character;
}
else {
// unique selectoritem is found, add it to the list
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
// in case the last character was '>', '+' or '~', we need to add it as a separate item
STORABLE_SPLIT_CHARACTER[character] && (list[list.length]=character);
}
}
// add the last item
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
return list;
};
_batchEmit = function() {
// we will exactly define the 'UI:'-event --> Itags will have another emitterName
MUTATION_EVENTS.each(function (mutationEvents, vnode) {
var domNode = vnode.domNode;
if (mutationEvents[EV_REMOVED]) {
domNode.emit('UI:'+EV_REMOVED);
}
else if (mutationEvents[EV_INSERTED]) {
domNode.emit('UI:'+EV_INSERTED);
}
else {
// contentchange and attributechanges can go hand in hand
mutationEvents.each(function(value, evt) {
domNode.emit('UI:'+evt, (evt===EV_CONTENT_CHANGE) ? null : {changed: value});
});
}
});
MUTATION_EVENTS.clear();
BATCH_WILL_RUN = false;
};
_emitDestroyChildren = function(vnode) {
var children = vnode.vChildren,
len = children.length,
i, vChild;
for (i=0; i<len; i++) {
vChild = children[i];
vChild._emit(EV_REMOVED);
}
};
_markRemoved = function(vnode) {
var vChildNodes = vnode.vChildNodes,
len, i, vChildNode;
if (vnode.nodeType===1) {
Object.protectedProp(vnode, 'removedFromDOM', true);
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && _markRemoved(vChildNode);
}
}
}
};
/**
* A safe way to remove a dom-node, even if it is not present.
* Will not throw a JS error when the "to be removed" node isn't in the dom
*
* @method _tryRemoveDomNode
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param childDomNode {DOMNode} the node that needs to be removed
* @since 0.0.1
*/
_tryRemoveDomNode = function(parentDomNode, childDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && childDomNode.isItag && childDomNode.isItag()) {
DOCUMENT._itagList.remove(childDomNode);
}
try {
parentDomNode._removeChild(childDomNode);
}
catch(err) {}
};
/**
* A safe way to replace a dom-node, even if the "to be replaced"-node it is not present.
* Will not throw a JS error when the "to be replaced" node isn't in the dom. in that case, the new node will
* be appended
*
* @method _tryReplaceChild
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param newChildDomNode {DOMNode} the node that needs to be replaced
* @param oldChildDomNode {DOMNode} the node that needs to be inserted
* @since 0.0.1
*/
_tryReplaceChild = function(parentDomNode, newChildDomNode, oldChildDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && oldChildDomNode.isItag && oldChildDomNode.isItag()) {
DOCUMENT._itagList.remove(oldChildDomNode);
}
try {
parentDomNode._replaceChild(newChildDomNode, oldChildDomNode);
}
catch(err) {
// if the childDomNode isn;t there any more - for whatever reason - the we need to append the newChildNode
parentDomNode._appendChild(newChildDomNode);
}
};
vNodeProto = window._ITSAmodules.VNode = {
/**
* Check whether the vnode's domNode is equal, or contains the specified Element.
*
* @method contains
* @return {Boolean} whether the vnode's domNode is equal, or contains the specified Element.
* @since 0.0.1
*/
contains: function(otherVNode, noProtectedSearch) {
var instance = this;
if (otherVNode && (otherVNode.destroyed || (noProtectedSearch && otherVNode._systemNode))) {
return false;
}
while (otherVNode && (otherVNode!==instance)) {
otherVNode = otherVNode.vParent;
if (otherVNode && noProtectedSearch && (otherVNode._systemNode || (otherVNode.isItag && otherVNode.domNode.contentHidden))) {
return false;
}
}
return (otherVNode===instance);
},
/**
* Empties the vnode.
*
* Syncs with the dom.
*
* @method empty
* @param [full=false] {Boolean} whether system-nodes should be removed as well
* @chainable
* @since 0.0.1
*/
empty: function(full) {
delete this._scripts;
return this._setChildNodes([], null, full);
},
/**
* Returns the first child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method firstOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the first child-vnode or null when not present
* @since 0.0.1
*/
firstOfVChildren: function(cssSelector) {
var instance = this,
found, i, len, vChildren, element;
if (!cssSelector) {
return instance.vFirstElementChild;
}
vChildren = instance.vChildren;
len = vChildren.length;
for (i=0; !found && (i<len); i++) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
return found;
},
/**
* Gets the innerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* Only valid for nodetype=1 (HTMLElements)
*
* @method getHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String|undefined} the innerHTML without the elements specified, or `undefined` when not an HTMLElement
* @since 0.0.1
*/
getHTML: function(exclude, includeSystemNodes) {
var instance = this,
html, vChildNodes, len, i, vChildNode;
if (instance.nodeType===1) {
if (exclude) {
Array.isArray(exclude) || (exclude=[exclude]);
}
html = '';
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
switch (vChildNode.nodeType) {
case 1:
(exclude && exclude.contains(vChildNode.domNode)) || (!includeSystemNodes && vChildNode._systemNode) || (html+=vChildNode.getOuterHTML(exclude, includeSystemNodes));
break;
case 3:
html += vChildNode.text.replace(/</g, '<').replace(/>/g, '>');
break;
case 8:
html += '<!--' + vChildNode.text.replace(/</g, '<').replace(/>/g, '>') + '-->';
}
}
}
return html;
},
/**
* Gets the outerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* @method getOuterHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String} the outerHTML
* @since 0.0.1
*/
getOuterHTML: function(exclude, includeSystemNodes) {
var instance = this,
html,
attrs = instance.attrs;
if (instance.nodeType===1) {
if (instance.nodeType!==1) {
return instance.textContent;
}
html = '<' + instance.tag.toLowerCase();
attrs.each(function(value, key) {
html += ' '+key+'="'+value+'"';
});
instance.isVoid && (html += '/');
html += '>';
if (!instance.isVoid) {
html += ((!includeSystemNodes && instance.isItag) ? '' : instance.getHTML(exclude, includeSystemNodes)) + '</' + instance.tag.toLowerCase() + '>';
}
}
return html;
},
/**
* Checks whether the vnode has any vChildNodes (nodeType of 1, 3 or 8).
*
* @method hasVChildNodes
* @return {Boolean} whether the vnode has any vChildNodes.
* @since 0.0.1
*/
hasVChildNodes: function() {
return this.vChildNodes ? (this.vChildNodes.length>0) : false;
},
/**
* Checks whether the vnode has any vChildren (vChildNodes with nodeType of 1).
*
* @method hasVChildren
* @return {Boolean} whether the vnode has any vChildren.
* @since 0.0.1
*/
hasVChildren: function() {
return this.vChildNodes ? (this.vChildren.length>0) : false;
},
/**
* Checks whether the className is present on the vnode.
*
* @method hasClass
* @param className {String|Array} the className to check for. May be an Array of classNames, which all needs to be present.
* @return {Boolean} whether the className (or classNames) is present on the vnode
* @since 0.0.1
*/
hasClass: function(className) {
var instance = this,
check = function(cl) {
return !!instance.classNames[cl];
};
if (!instance.classNames) {
return false;
}
if (typeof className === STRING) {
return check(className);
}
else if (Array.isArray(className)) {
return className.every(check);
}
return false;
},
/**
* Returns the last child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method lastOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the last child-vnode or null when not present
* @since 0.0.1
*/
lastOfVChildren: function(cssSelector) {
var vChildren = this.vChildren,
found, i, element;
if (vChildren) {
if (!cssSelector) {
return this.vLastElementChild;
}
for (i=vChildren.length-1; !found && (i>=0); i--) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
}
return found;
},
/**
* Checks whether the vnode matches one of the specified selectors. `selectors` can be one, or multiple css-selectors,
* separated by a `comma`. For example: "#myid li.red blue" is one selector, "div.red, div.blue, div.green" are three selectors.
*
* @method matchesSelector
* @param selectors {String} one or more css-selectors
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches one of the selectors
* @since 0.0.1
*/
matchesSelector: function(selectors, relatedVNode) {
var instance = this;
if (instance.nodeType!==1) {
return false;
}
selectors = selectors.split(',');
// we can use Array.some, because there won't be many separated selectoritems,
// so the final invocation won't be delayed much compared to looping
return selectors.some(function(selector) {
return _matchesOneSelector(instance, selector, relatedVNode);
});
},
/**
* Reloads the DOM-attribute into the vnode.
*
* @method matchesSelector
* @param attributeName {String} the name of the attribute to be reloaded.
* @return {Node} the domNode that was reloaded.
* @since 0.0.1
*/
reloadAttr: function(attributeName) {
var instance = this,
domNode = instance.domNode,
attributeValue = domNode._getAttribute(attributeName),
attrs = instance.attrs,
extractStyle, extractClass;
if (instance.nodeType===1) {
attributeValue || (attributeValue='');
if (attributeValue==='') {
delete attrs[attributeName];
// in case of STYLE attributeName --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attributeName --> special treatment
(attributeName===CLASS) && (instance.classNames={});
// in case of ID attributeName --> special treatment
if ((attributeName===ID) && (instance.id)) {
delete nodeids[instance.id];
delete instance.id;
}
}
else {
attrs[attributeName] = attributeValue;
// in case of STYLE attributeName --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(attributeValue);
attributeValue = extractStyle.attrStyle;
if (attributeValue) {
attrs.style = attributeValue;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attributeName --> special treatment
extractClass = extractor.extractClass(attributeValue);
attributeValue = extractClass.attrClass;
if (attributeValue) {
attrs[CLASS] = attributeValue;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (instance.id!==attributeValue) && (delete nodeids[instance.id]);
instance.id = attributeValue;
nodeids[attributeValue] = domNode;
}
}
}
return domNode;
},
/**
* Returns the vnode's style in a serialized form: the way it appears in the dom.
*
* @method serializeStyles
* @return {String} vnode's style
* @since 0.0.1
*/
serializeStyles: function() {
return extractor.serializeStyles(this.styles);
},
/**
* Sets the vnode's and dom-nodes inner HTML.
*
* Syncs with the dom. Can be invoked multiple times without issues.
*
* @method setHTML
* @param content {String} the innerHTML
* @param [suppressItagRender] {Boolean} to suppress Itags from rendering
* @param [allowScripts=false] {Boolean} whether scripts are allowed --> these should be defined with `xscript` instead of `script`
* @chainable
* @since 0.0.1
*/
setHTML: function(content, suppressItagRender, allowScripts) {
var instance = this;
instance._setChildNodes(htmlToVNodes(content, vNodeProto, instance.ns, null, suppressItagRender, allowScripts), suppressItagRender);
return instance;
},
/**
* Syncs the vnode's nodeid (if available) inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom. Can be invoked multiple times without issues.
*
* @method storeId
* @chainable
* @since 0.0.1
*/
storeId: function() {
// store node/vnode inside WeakMap:
var instance = this;
instance.id ? (nodeids[instance.id]=instance.domNode) : (delete nodeids[instance.id]);
return instance;
},
//---- private ------------------------------------------------------------------
_addToTaglist: function() {
var instance = this,
itagList;
if (instance.isItag) {
itagList = DOCUMENT.getItags(); // also reads the dom if the list isn't build yet
instance._data || Object.protectedProp(instance, '_data', {});
if (!instance._data.ce_destroyed && !itagList.contains(instance.domNode)) {
itagList.push(instance.domNode);
}
}
},
/**
* Adds a vnode to the end of the list of vChildNodes.
*
* Syncs with the DOM.
*
* @method _appendChild
* @param VNode {vnode} vnode to append
* @private
* @return {Node} the Node that was appended
* @since 0.0.1
*/
_appendChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
size, noProccessScript, scriptContent;
if ((VNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (VNode.attrs && VNode.attrs.src) ? VNode.attrs.src : VNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
VNode._moveToParent(instance);
instance.domNode._appendChild(domNode);
if (VNode.nodeType===3) {
size = instance.vChildNodes.length;
instance._normalize();
// if the size changed, then the domNode was merged
(size===instance.vChildNodes.length) || (domNode=instance.vChildNodes[instance.vChildNodes.length-1].domNode);
}
if (VNode.nodeType===1) {
VNode._addToTaglist();
VNode._emit(EV_INSERTED);
}
return domNode;
}
},
/**
* Removes the vnode from its parent vChildNodes- and vChildren-list.
*
* Does NOT sync with the dom.
*
* @method _deleteFromParent
* @private
* @chainable
* @since 0.0.1
*/
_deleteFromParent: function() {
var instance = this,
vParent = instance.vParent;
if (vParent && vParent.vChildNodes) {
vParent.vChildNodes.remove(instance);
// force to recalculate the vChildren on a next call:
(instance.nodeType===1) && (vParent._vChildren=null);
}
return instance;
},
/**
* Cleans up (empties) vnode's `_data`
*
* @method _cleanData
* @private
* @chainable
* @since 0.0.1
*/
_cleanData: function() {
var instance = this,
data = instance._data;
data && data.each(
function(value, key) {
delete data[key];
}
);
return instance;
},
/**
* Cleans up (removes) duplicated `style` definitions.
*
* @method _cleanupStyle
* @private
* @chainable
* @since 0.0.1
*/
_cleanupStyle: function() {
var instance = this,
compare = [],
removal = [],
vChildren = instance.vChildren,
len = vChildren.length,
vChild, i, styleContent, vChildInner;
for (i=0; i<len; i++) {
vChild = vChildren[i];
if (vChild.tag==='STYLE') {
vChildInner = vChild.vChildNodes[0];
if (vChildInner) {
styleContent = vChildInner.text;
if (compare.contains(styleContent)) {
removal[removal.length] = vChild;
}
else {
compare[compare.length] = styleContent;
}
}
else {
// empty style-tag
removal[removal.length] = vChild;
}
}
}
removal.forEach(function(vnode) {
instance._removeChild(vnode);
});
},
/**
* Destroys the vnode and all its vnode-vChildNodes.
* Removes it from its vParent.vChildNodes list,
* also removes its definitions inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom.
*
* @method _destroy
* @private
* @chainable
* @since 0.0.1
*/
_destroy: function(silent) {
var instance = this,
vChildNodes = instance.vChildNodes,
len, i, vChildNode, vParent, treeNodes;
if (!instance.destroyed) {
if (!silent) {
// Because we don't want to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
later(function() {
instance._emit(EV_REMOVED, null, null, null, true);
}, 5);
}
Object.protectedProp(instance, 'destroyed', true);
// first: determine the dom-tree, which module `event-dom` needs to determine where the node was before it was destroyed:
treeNodes = [instance];
vParent = instance.vParent;
while (vParent) {
treeNodes[treeNodes.length] = vParent;
vParent = vParent.vParent;
}
// mark all its vChildNodes so we can see if the node is in the DOM
_markRemoved(instance);
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && instance.isItag) {
DOCUMENT._itagList.remove(instance.domNode);
}
instance._scripts && (instance._scripts.length=0);
// The definite cleanup needs to be done after a timeout:
// someone might need to handle the Element when removed (fe to cleanup specific things)
later(function() {
instance._cleanData();
if (instance.nodeType===1) {
// _destroy all its vChildNodes
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && vChildNode._destroy(true);
}
}
}
instance._vChildren = null;
// explicitely set instance.domNode._vnode and instance.domNode to null in order to prevent problems with the GC (we break the circular reference)
delete instance.domNode._vnode;
// if valid id, then _remove the DOMnodeRef from internal hash
instance.id && delete nodeids[instance.id];
}, silent ? 0 : DESTROY_DELAY);
instance._deleteFromParent();
// Do not make domNode `null` --> it could be used even when not in the dom
}
return instance;
},
_emit: function(evt, attribute, newValue, prevValue, destroyEvt) {
/**
* Emitted by every Element that gets inserted.
*
* @event nodeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets removed.
*
* @event noderemove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets its content changed (innerHTML/innerText).
*
* @event nodecontentchange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute inserted.
*
* @event attributeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* </ul>
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute removed.
*
* @event attributeremove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Strings of the attributeNames that are removed
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute changed.
*
* @event attributechange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* <li>prevValue</li>
* </ul>
* @since 0.1
*/
var instance = this,
silent, attrMutations, mutationEvents, mutation, vParent;
if (!DOCUMENT.hasMutationSubs || (instance.nodeType!==1)) {
return;
}
silent = !!DOCUMENT._suppressMutationEvents;
if (!silent && (destroyEvt || !instance.destroyed)) {
// Because we don't wannt to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
mutationEvents = MUTATION_EVENTS.get(instance) || {};
if (attribute) {
attrMutations = mutationEvents[evt] || [];
if (evt===EV_ATTRIBUTE_REMOVED) {
mutation = attribute;
}
else {
mutation = {
attribute: attribute
};
if ((evt===EV_ATTRIBUTE_INSERTED) || (evt===EV_ATTRIBUTE_CHANGED)) {
mutation.newValue = newValue;
}
if ((evt===EV_ATTRIBUTE_CHANGED) && prevValue) {
mutation.prevValue = prevValue;
}
}
attrMutations.push(mutation);
mutationEvents[evt] = attrMutations;
}
else {
mutationEvents[evt] = true;
}
MUTATION_EVENTS.set(instance, mutationEvents);
// now set all parent to have a nodecontentchange:
vParent = instance.vParent;
vParent && vParent._emit(EV_CONTENT_CHANGE);
// in case of removal we need to emit EV_REMOVED for all children right now
// for they will be actually removed silently after a delay of 1 minute
(evt===EV_REMOVED) && _emitDestroyChildren(instance);
if (!BATCH_WILL_RUN) {
BATCH_WILL_RUN = true;
async(function() {
_batchEmit();
});
}
}
return instance;
},
/**
* Inserts `newVNode` before `refVNode`.
*
* Syncs with the DOM.
*
* @method _insertBefore
* @param newVNode {vnode} vnode to insert
* @param refVNode {vnode} The vnode before which newVNode should be inserted.
* @private
* @return {Node} the Node being inserted (equals domNode)
* @since 0.0.1
*/
_insertBefore: function(newVNode, refVNode) {
var instance = this,
domNode = newVNode.domNode,
index = instance.vChildNodes.indexOf(refVNode),
noProccessScript, scriptContent;
if ((newVNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (newVNode.attrs && newVNode.attrs.src) ? newVNode.attrs.src : newVNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
if (index!==-1) {
newVNode._moveToParent(instance, index);
instance.domNode._insertBefore(domNode, refVNode.domNode);
(newVNode.nodeType===3) && instance._normalize();
if (newVNode.nodeType===1) {
newVNode._addToTaglist();
newVNode._emit(EV_INSERTED);
}
return domNode;
}
else {
console.warn('trying to insert before, but no ref-node found. Will append the new node');
return instance._appendChild(newVNode);
}
}
},
/**
* Moves the vnode from its current parent.vChildNodes list towards a new parent vnode at the specified position.
*
* Does NOT sync with the dom.
*
* @method _moveToParent
* @param parentVNode {vnode} the parent-vnode
* @param [index] {Number} the position of the child. When not specified, it will be appended.
* @private
* @chainable
* @since 0.0.1
*/
_moveToParent: function(parentVNode, index) {
var instance = this,
vParent = instance.vParent;
instance._deleteFromParent();
instance.vParent = parentVNode;
parentVNode.vChildNodes || (parentVNode.vChildNodes=[]);
(typeof index==='number') ? parentVNode.vChildNodes.insertAt(instance, index) : (parentVNode.vChildNodes[parentVNode.vChildNodes.length]=instance);
// force to recalculate the vChildren on a next call:
vParent && (instance.nodeType===1) && (vParent._vChildren = null);
// force to recalculate the vChildren on a next call:
parentVNode && (instance.nodeType===1) && (parentVNode._vChildren=null);
return instance;
},
/**
* Removes empty TextNodes and merges following TextNodes inside the vnode.
*
* Syncs with the dom.
*
* @method _normalize
* @private
* @chainable
* @since 0.0.1
*/
_normalize: function() {
var instance = this,
domNode = instance.domNode,
vChildNodes = instance.vChildNodes,
changed = false,
i, preChildNode, vChildNode;
if (!instance._unNormalizable && vChildNodes) {
for (i=vChildNodes.length-1; i>=0; i--) {
vChildNode = vChildNodes[i];
preChildNode = vChildNodes[i-1]; // i will get the value `-1` eventually, which leads into undefined preChildNode
if (vChildNode.nodeType===3) {
if (vChildNode.text==='') {
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
else if (preChildNode && preChildNode.nodeType===3) {
preChildNode.text += vChildNode.text;
preChildNode.domNode.nodeValue = unescapeEntities(preChildNode.text);
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
}
}
}
changed && instance._emit(EV_CONTENT_CHANGE);
return instance;
},
/**
* Makes the vnode `normalizable`. Could be set to `false` when batch-inserting nodes, while `normalizaing` manually at the end.
* Afterwards, you should always reset `normalizable` to true.
*
* @method _normalizable
* @param value {Boolean} whether the vnode should be normalisable.
* @private
* @chainable
* @since 0.0.1
*/
_normalizable: function(value) {
var instance = this;
value ? (delete instance._unNormalizable) : (instance._unNormalizable=true);
return instance;
},
/**
* Prevents MutationObserver from making the dom sync with the vnode.
* Should be used when manipulating the dom from within the vnode itself (to preventing looping)
*
* @method _noSync
* @chainable
* @private
* @since 0.0.1
*/
_noSync: function() {
var instance = this;
if (!instance._nosync) {
instance._nosync = true;
async(function() {
instance._nosync = false;
});
}
return instance;
},
/**
* Removes the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _removeAttr
* @param attributeName {String}
* @private
* @chainable
* @since 0.0.1
*/
_removeAttr: function(attributeName, suppressItagRender) {
var instance = this,
attributeNameSplitted, ns;
if (instance.isItag && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
console.warn('Not allowed to remove the attribute '+attributeName);
return instance;
}
if (instance.attrs[attributeName]!==undefined) {
delete instance.attrs[attributeName];
// in case of STYLE attribute --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attribute --> special treatment
(attributeName===CLASS) && (instance.classNames={});
if (attributeName===ID) {
delete nodeids[instance.id];
delete instance.id;
}
instance._emit(EV_ATTRIBUTE_REMOVED, attributeName);
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
instance.domNode._removeAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName);
}
else {
instance.domNode._removeAttribute(attributeName);
}
}
return instance;
},
/**
* Removes the vnode's child-vnode from its vChildren and the DOM.
*
* Syncs with the DOM.
*
* @method removeChild
* @param VNode {vnode} the child-vnode to remove
* @private
* @since 0.0.1
*/
_removeChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
hadFocus = domNode && domNode.hasFocus && domNode.hasFocus() && (VNode.attrs['fm-lastitem']==='true'),
parentVNode = VNode.vParent;
VNode._destroy();
_tryRemoveDomNode(instance.domNode, VNode.domNode);
instance._normalize();
// now, reset the focus on focusmanager when needed:
if (hadFocus) {
while (parentVNode && !parentVNode.attrs['fm-manage']) {
parentVNode = parentVNode.vParent;
}
parentVNode && parentVNode.domNode.focus();
}
},
/**
* Replaces the current vnode at the parent.vChildNode list by `newVNode`
*
* Does NOT sync with the dom.
*
* @method _replaceAtParent
* @param newVNode {Object} the new vnode which should take over the place of the current vnode
* @private
* @chainable
* @since 0.0.1
*/
_replaceAtParent: function(newVNode) {
var instance = this,
vParent = instance.vParent,
vChildNodes, index;
if (vParent && (vChildNodes=vParent.vChildNodes)) {
index = vChildNodes.indexOf(instance);
// force to recalculate the vChildren on a next call:
((instance.nodeType===1) || (newVNode.nodeType===1)) && (instance.vParent._vChildren=null);
vChildNodes[index] = newVNode;
}
return instance._destroy();
},
/**
* Sets the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _setAttr
* @param attributeName {String}
* @param value {String} the value for the attributeName
* @param [force=false] {Boolean} force the attribute to be set, even if restrictions would deny it
* @private
* @chainable
* @since 0.0.1
*/
_setAttr: function(attributeName, value, force, suppressItagRender) {
var instance = this,
extractStyle, extractClass,
attrs = instance.attrs,
prevVal = attrs[attributeName],
domNode = instance.domNode,
attributeNameSplitted, ns;
if (instance.isItag && !force && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
if (prevVal!=value) {
console.warn('Not allowed to set the attribute '+attributeName);
}
return instance;
}
// don't check by !== --> value isn't parsed into a String yet
if (prevVal && ((value===undefined) || (value===null))) {
instance._removeAttr(attributeName, suppressItagRender);
return instance;
}
// attribute-values are always Strings:
value = String(value);
// attribute-values will be stored without " or '
value = value.replace(/"/g, '"').replace(/'/g, "'");
if (prevVal!=value) {
attrs[attributeName] = value;
// in case of STYLE attribute --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(value);
value = extractStyle.attrStyle;
if (value) {
attrs.style = value;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attribute --> special treatment
extractClass = extractor.extractClass(value);
value = extractClass.attrClass;
if (value) {
attrs[CLASS] = value;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (delete nodeids[instance.id]);
instance.id = value;
nodeids[value] = domNode;
}
instance._emit(prevVal ? EV_ATTRIBUTE_CHANGED : EV_ATTRIBUTE_INSERTED, attributeName, value, prevVal);
// when set in the dom --> quotes need to be set as "
value = value.replace(/"/g, '"');
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
domNode._setAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName, value);
}
else {
domNode._setAttribute(attributeName, value);
}
}
return instance;
},
/**
* Redefines the attributes of both the vnode as well as its related dom-node. The new
* definition replaces any previous attributes (without touching unmodified attributes).
* the `is` attribute cannot be changed for itags.
*
* Syncs the new vnode's attributes with the dom.
*
* @method _setAttrs
* @param newAttrs {Object|Array} the new attributes to be set
* @private
* @chainable
* @since 0.0.1
*/
_setAttrs: function(newAttrs, suppressItagRender) {
// does sync the DOM
var instance = this,
attrsObj, attr, attrs, i, key, keys, len, value;
if (instance.nodeType!==1) {
return;
}
instance._noSync();
attrs = instance.attrs;
attrs.id && (delete nodeids[attrs.id]);
if (Object.isObject(newAttrs)) {
attrsObj = newAttrs;
}
else {
attrsObj = {};
len = newAttrs.length;
for (i=0; i<len; i++) {
attr = newAttrs[i];
attrsObj[attr.name] = attr.value;
}
}
if (instance.isItag && !suppressItagRender) {
if (attrs.is) {
attrsObj.is = attrs.is;
}
else {
delete attrsObj.is;
delete attrsObj.Is;
delete attrsObj.iS;
delete attrsObj.IS;
}
}
// first _remove the attributes that are no longer needed.
// quickest way for object iteration: http://jsperf.com/object-keys-iteration/20
keys = Object.keys(attrs);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
attrsObj[key] || instance._removeAttr(key, suppressItagRender);
}
// next: every attribute that differs: redefine
keys = Object.keys(attrsObj);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
value = attrsObj[key];
(attrs[key]===value) || instance._setAttr(key, value, suppressItagRender);
}
return instance;
},
/**
* Redefines the childNodes of both the vnode as well as its related dom-node. The new
* definition replaces any previous nodes. (without touching unmodified nodes).
*
* Syncs the new vnode's childNodes with the dom.
*
* @method _setChildNodes
* @param newVChildNodes {Array} array with vnodes which represent the new childNodes
* @private
* @chainable
* @since 0.0.1
*/
_setChildNodes: function(newVChildNodes, suppressItagRender, removeSystemElements) {
// does sync the DOM
var instance = this,
vChildNodes = instance.vChildNodes || [],
domNode = instance.domNode,
forRemoval = [],
i, oldChild, newChild, newLength, len, len2, childDomNode, nodeswitch, bkpAttrs, cleanupStyle,
process, bkpChildNodes, needNormalize, prevSuppress, scriptContent, _scripts, scriptLen, j;
instance._noSync();
// first: reset ._vChildren --> by making it empty, its getter will refresh its list on a next call
instance._vChildren = null;
// if newVChildNodes is undefined, then we assume it to be empty --> an empty array
newVChildNodes || (newVChildNodes=[]);
// quickest way to loop through array is by using for loops: http://jsperf.com/array-foreach-vs-for-loop/5
len = vChildNodes.length;
// we need to add the systemNodes -if any- to the new newVChildNodes: they should be retained.
// SystemNodes are always places at the beginning, so they won't get reshuffled:
if (!removeSystemElements) {
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
if (oldChild._systemNode) {
newVChildNodes.insertAt(oldChild, i);
}
else {
break;
}
}
}
newLength = newVChildNodes.length;
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
childDomNode = oldChild.domNode;
if (i < newLength) {
newChild = newVChildNodes[i];
newChild.vParent || (newChild.vParent=instance);
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
/*jshint boss:true */
switch (nodeswitch=NODESWITCH[oldChild.nodeType][newChild.nodeType]) {
/*jshint boss:false */
case 1: // oldNodeType==Element, newNodeType==Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
if ((oldChild.tag!==newChild.tag) ||
((oldChild.tag===newChild.tag) && oldChild.isItag && (oldChild.attrs.is!==newChild.attrs.is))) {
// new tag --> completely replace
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
/*jshint proto:true */
oldChild.isItag && oldChild.domNode.destroyUI && oldChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
newChild.attrs = {}; // reset to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
oldChild._replaceAtParent(newChild);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
else {
// same tag --> only update what is needed
// NOTE: when this._unchangableAttrs exists, an itag-element syncs its UI -->
if (oldChild._data) {
// we might need to set the class `itag-rendered` when the attributeData says so:
// this happens when an itag gets refreshed with an unrendered definition
if (oldChild._data.itagRendered && !newChild.hasClass('itag-rendered')) {
newChild.classNames['itag-rendered'] = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' '+'itag-rendered';
}
else {
newChild.attrs[CLASS] = 'itag-rendered';
}
}
// we might need to set the class `focussed` when the attributeData says so:
// this happens when an itag gets rerendered: its renderFn doesn't know if any elements
// were focussed
if (oldChild._data.focussed && !newChild.hasClass('focussed')) {
newChild.classNames.focussed = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' ' + 'focussed';
}
else {
newChild.attrs[CLASS] = 'focussed';
}
}
if (oldChild._data['fm-tabindex']) {
// node has the tabindex set by the focusmanager,
// but that info might got lost with re-rendering of the new element
newChild.attrs.tabindex = '0';
}
}
if (oldChild.isItag) {
prevSuppress = DOCUMENT._suppressMutationEvents || false;
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
/*jshint proto:true */
newChild.domNode.destroyUI && newChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
oldChild._setAttrs(newChild.attrs, suppressItagRender);
newChild._destroy(true); // destroy through the vnode and removing from DOCUMENT._itagList
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(prevSuppress);
}
else {
oldChild._setAttrs(newChild.attrs, suppressItagRender);
// next: sync the vChildNodes:
oldChild._setChildNodes(newChild.vChildNodes, suppressItagRender);
}
// reset ref. to the domNode, for it might have been changed by newChild:
oldChild.id && (nodeids[oldChild.id]=childDomNode);
newVChildNodes[i] = oldChild;
}
}
break;
case 2: // oldNodeType==Element, newNodeType==TextNode
// case2 and case3 should be treated the same
case 3: // oldNodeType==Element, newNodeType==Comment
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
oldChild._replaceAtParent(newChild);
instance._emit(EV_CONTENT_CHANGE);
break;
case 4: // oldNodeType==TextNode, newNodeType==Element
// case4 and case7 should be treated the same
case 7: // oldNodeType==Comment, newNodeType==Element
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
// oldChild.isVoid = newChild.isVoid;
// delete oldChild.text;
instance._emit(EV_CONTENT_CHANGE);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
break;
case 5: // oldNodeType==TextNode, newNodeType==TextNode
// case5 and case9 should be treated the same
case 9: // oldNodeType==Comment, newNodeType==Comment
if (oldChild.text!==newChild.text) {
oldChild.text = newChild.text;
oldChild.domNode.nodeValue = unescapeEntities(newChild.text);
instance._emit(EV_CONTENT_CHANGE);
}
newVChildNodes[i] = oldChild;
break;
case 6: // oldNodeType==TextNode, newNodeType==Comment
// case6 and case8 should be treated the same
case 8: // oldNodeType==Comment, newNodeType==TextNode
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = oldChild.vParent;
instance._emit(EV_CONTENT_CHANGE);
}
if ((nodeswitch===2) || (nodeswitch===5) || (nodeswitch===8)) {
needNormalize = true;
}
}
else {
// _remove previous definition
_tryRemoveDomNode(domNode, oldChild.domNode);
// the oldChild needs to be removed, however, this cannot be done right now, for it would effect the loop
// so we store it inside a hash to remove it later
forRemoval[forRemoval.length] = oldChild;
}
}
// now definitely remove marked childNodes:
len2 = forRemoval.length;
for (i=0; i<len2; i++) {
forRemoval[i]._destroy();
}
// now we add all new vChildNodes that go beyond `len`:
for (i = len; i < newLength; i++) {
newChild = newVChildNodes[i];
newChild.vParent = instance;
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
switch (newChild.nodeType) {
case 1: // Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
domNode._appendChild(newChild.domNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
break;
case 3: // TextNode
needNormalize = true;
// we need to break through --> no `break`
/* falls through */
default: // TextNode or CommentNode
// newChild.domNode.nodeValue = newChild.text;
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
domNode._appendChild(newChild.domNode);
instance._emit(EV_CONTENT_CHANGE);
}
newChild.storeId();
}
instance.vChildNodes = newVChildNodes;
needNormalize && instance._normalize();
cleanupStyle && instance._cleanupStyle();
return instance;
},
_setUnchangableAttrs: function(unchangableObj) {
this._unchangableAttrs = unchangableObj;
}
};
//---- properties ------------------------------------------------------------------
/**
* A hash of all the `attributes` of the vnode's representing dom-node.
*
* @property attrs
* @type Object
* @since 0.0.1
*/
/**
* Hash with all the classes of the vnode. Every class represents a key, all values are set `true`.
*
* @property classNames
* @type Object
* @since 0.0.1
*/
/**
* The `id` of the vnode's representing dom-node (if any).
*
* @property id
* @type String
* @since 0.0.1
*/
/**
* Tells whether tag is a void Element. Examples are: `br`, `img` and `input`. Non-void Elements are f.e. `div` and `table`.
* For TextNodes and CommentNodes, this property is `undefined`.
*
* @property isVoid
* @type Boolean
* @since 0.0.1
*/
/**
* The `nodeType` of the vnode's representing dom-node (1===ElementNode, 3===TextNode, 8===CommentNode).
*
* @property nodeType
* @type Number
* @since 0.0.1
*/
/**
* The `tag` of the vnode's representing dom-node (allways uppercase).
*
* @property tag
* @type String
* @since 0.0.1
*/
/**
* The `content` of the vnode's representing dom-node, in case it is a TextNode or CommentNode.
* Equals dom-node.nodeValue.
*
* Is `undefined` for ElementNodes.
*
* @property text
* @type String
* @since 0.0.1
*/
/**
* Hash with all the childNodes (vnodes). vChildNodes are any kind of vnodes (nodeType===1, 3 or 8)
*
* @property vChildNodes
* @type Array
* @since 0.0.1
*/
/**
* The underlying `dom-node` that the vnode represents.
*
* @property domNode
* @type domNode
* @since 0.0.1
*/
/**
* vnode's parentNode (defined as a vnode itself).
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
Object.defineProperties(vNodeProto, {
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property innerHTML
* @type String
* @since 0.0.1
*/
innerHTML: {
get: function() {
return this.getHTML();
},
set: function(v) {
this.setHTML(v);
}
},
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property nodeValue
* @type String
* @since 0.0.1
*/
nodeValue: {
get: function() {
var instance = this;
return ((instance.nodeType===3) || (instance.nodeType===8)) ? instance.text : null;
},
set: function(v) {
var instance = this,
newTextContent, prevTextContent;
if ((instance.nodeType===3) || (instance.nodeType===8)) {
prevTextContent = instance.domNode.textContent;
instance.domNode.textContent = v;
// set .text AFTER the dom-node is updated --> the content might be escaped!
newTextContent = instance.text = instance.domNode.textContent;
(newTextContent!==prevTextContent) && instance._emit(EV_CONTENT_CHANGE);
}
}
},
/**
* Gets or sets the outerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property outerHTML
* @type String
* @since 0.0.1
*/
outerHTML: {
get: function() {
return this.getOuterHTML();
},
set: function(v) {
var instance = this,
vParent = instance.vParent,
id = instance.attrs.id,
vnode, vnodes, bkpAttrs, bkpChildNodes, i, len, vChildNodes, isLastChildNode, index, refDomNode;
if ((instance.nodeType!==1) || !vParent) {
return;
}
instance._noSync();
vChildNodes = vParent.vChildNodes;
index = vChildNodes.indexOf(instance);
isLastChildNode = (index===(vChildNodes.length-1));
isLastChildNode || (refDomNode=vChildNodes[index+1].domNode);
vnodes = htmlToVNodes(v, vNodeProto, vParent.ns, vParent);
len = vnodes.length;
if (len>0) {
// the first vnode will replace the current instance:
vnode = vnodes[0];
if (vnode.nodeType===1) {
if (vnode.tag!==instance.tag) {
// new tag --> completely replace
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
id && (delete nodeids[id]);
vnode.attrs = {}; // reset to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
// vnode.attrs = bkpAttrs;
// vnode.vChildNodes = bkpChildNodes;
vnode.id && (nodeids[vnode.id]=vnode.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
else {
instance._setAttrs(vnode.attrs);
instance._setChildNodes(vnode.vChildNodes);
}
}
else {
id && (delete nodeids[id]);
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
}
for (i=1; i<len; i++) {
vnode = vnodes[i];
switch (vnode.nodeType) {
case 1: // Element
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
vnode.attrs = {}; // reset, to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._insertBefore(vnode.domNode, refDomNode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
break;
default: // TextNode or CommentNode
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._appendChild(vnode.domNode, refDomNode);
}
vnode.storeId();
vnode._moveToParent(vParent, index+i);
}
}
},
/**
* Gets or sets the innerContent of the Node as plain text.
*
* The setter syncs with the DOM.
*
* @property textContent
* @type String
* @since 0.0.1
*/
textContent: {
get: function() {
var instance = this,
text = '',
vChildNodes = instance.vChildNodes,
len, i, vChildNode;
if (instance.nodeType===1) {
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
text += (vChildNode.nodeType===3) ? vChildNode.text : ((vChildNode.nodeType===1) ? vChildNode.textContent : '');
}
}
else {
text = instance.text;
}
return text;
},
set: function(v) {
var vnode = Object.create(vNodeProto);
vnode.domNode = DOCUMENT.createTextNode(v);
// create circular reference:
vnode.domNode._vnode = vnode;
vnode.nodeType = 3;
vnode.text = vnode.domNode.textContent;
this._setChildNodes([vnode]);
}
},
/**
* Hash with all the children (vnodes). vChildren are vnodes that have a representing dom-node that is an HtmlElement (nodeType===1)
*
* @property vChildren
* @type Array
* @since 0.0.1
*/
vChildren: {
get: function() {
var instance = this,
children = instance._vChildren,
vChildNode, vChildNodes, i, len;
vChildNodes = instance.vChildNodes;
if (vChildNodes && !children) {
children = instance._vChildren = [];
len = vChildNodes.length;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
(vChildNode.nodeType===1) && (children[children.length]=vChildNode);
}
}
children || (children = instance._vChildren = []);
return children;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirst
* @type vnode
* @since 0.0.1
*/
vFirst: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstChild;
}
},
/**
* Reference to the first vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirstChild
* @type vnode
* @since 0.0.1
*/
vFirstChild: {
get: function() {
return (this.vChildNodes && this.vChildNodes[0]) || null;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vFirstElement
* @type vnode
* @since 0.0.1
*/
vFirstElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstElementChild;
}
},
/**
* Reference to the first vChild, where the related dom-node an Element (nodeType===1).
*
* @property vFirstElementChild
* @type vnode
* @since 0.0.1
*/
vFirstElementChild: {
get: function() {
return this.vChildren[0] || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLast
* @type vnode
* @since 0.0.1
*/
vLast: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastChild;
}
},
/**
* Reference to the last vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLastChild
* @type vnode
* @since 0.0.1
*/
vLastChild: {
get: function() {
var vChildNodes = this.vChildNodes;
return (vChildNodes && vChildNodes[vChildNodes.length-1]) || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vLastElement
* @type vnode
* @since 0.0.1
*/
vLastElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastElementChild;
}
},
/**
* Reference to the last vChild, where the related dom-node an Element (nodeType===1).
*
* @property vLastElementChild
* @type vnode
* @since 0.0.1
*/
vLastElementChild: {
get: function() {
var vChildren = this.vChildren;
return vChildren[vChildren.length-1] || null;
}
},
/**
* the Parent vnode
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
/**
* Reference to the next of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vNext
* @type vnode
* @since 0.0.1
*/
vNext: {
get: function() {
return _findNodeSibling(this, true);
}
},
/**
* Reference to the next of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vNextElement
* @type vnode
* @since 0.0.1
*/
vNextElement: {
get: function() {
return _findElementSibling(this, true);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vPrevious
* @type vnode
* @since 0.0.1
*/
vPrevious: {
get: function() {
return _findNodeSibling(this);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vPreviousElement
* @type vnode
* @since 0.0.1
*/
vPreviousElement: {
get: function() {
return _findElementSibling(this);
}
}
});
return vNodeProto;
};
},{"./attribute-extractor.js":92,"./html-parser.js":96,"./vdom-ns.js":98,"js-ext/extra/hashmap.js":50,"js-ext/extra/lightmap.js":51,"js-ext/lib/array.js":52,"js-ext/lib/object.js":53,"js-ext/lib/string.js":55,"polyfill":72,"utils/lib/timers.js":75}],100:[function(require,module,exports){
"use strict";
require('js-ext/lib/object.js');
var createHashMap = require('js-ext/extra/hashmap.js').createMap,
later = require('utils/lib/timers.js').later;
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.VDOM) {
return window._ITSAmodules.VDOM; // VDOM was already created
}
var DOCUMENT = window.document;
if (DOCUMENT.doctype.name==='html') {
require('./partials/extend-element.js')(window);
require('./partials/extend-document.js')(window);
// now parsing and virtualize the complete DOM:
require('./partials/node-parser.js')(window)(DOCUMENT.documentElement);
// if there is any Element with inline `transform` that is not compatible with the current browser:
// we can revert it into the right `transform`, because the vdom knows the right transform-name:
DOCUMENT.getAll('[style*="transform:"]').forEach(function(node) {
var vnode = node.vnode,
rightStyle = vnode.attrs.style;
// delete current definition, so that reset will do an update:
delete vnode.attrs.style;
// now reset:
vnode._setAttr('style', rightStyle);
});
// cleanup duplicated `style` elements - if any
// this can be done async with a small delay: no one will notice
later(function() {
var head = DOCUMENT.getElement('head');
head.vnode._cleanupStyle();
}, 500);
}
window._ITSAmodules.VDOM = true;
};
},{"./partials/extend-document.js":94,"./partials/extend-element.js":95,"./partials/node-parser.js":97,"js-ext/extra/hashmap.js":50,"js-ext/lib/object.js":53,"utils/lib/timers.js":75}],101:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":103,"js-ext/extra/hashmap.js":102,"polyfill/polyfill-base.js":108}],102:[function(require,module,exports){
module.exports=require(4)
},{}],103:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":102,"polyfill/polyfill-base.js":108,"utils":109}],104:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":108}],105:[function(require,module,exports){
module.exports=require(29)
},{}],106:[function(require,module,exports){
module.exports=require(14)
},{}],107:[function(require,module,exports){
module.exports=require(15)
},{}],108:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":106,"./lib/window.console.js":107}],109:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":110,"./lib/timers.js":111}],110:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":102,"polyfill/polyfill-base.js":114}],111:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":114}],112:[function(require,module,exports){
module.exports=require(14)
},{}],113:[function(require,module,exports){
module.exports=require(15)
},{}],114:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":112,"./lib/window.console.js":113}],115:[function(require,module,exports){
module.exports=require(14)
},{}],116:[function(require,module,exports){
module.exports=require(15)
},{}],117:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":115,"./lib/window.console.js":116}],118:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":121}],119:[function(require,module,exports){
module.exports=require(14)
},{}],120:[function(require,module,exports){
module.exports=require(15)
},{}],121:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":119,"./lib/window.console.js":120}],122:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],123:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":127,"js-ext/extra/hashmap.js":124,"polyfill/polyfill-base.js":133}],124:[function(require,module,exports){
module.exports=require(4)
},{}],125:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":126,"../lib/object.js":127,"./classes.js":123,"js-ext/extra/hashmap.js":124,"polyfill/lib/weakmap.js":131}],126:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":133}],127:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":124,"polyfill/polyfill-base.js":133,"utils":134}],128:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":133}],129:[function(require,module,exports){
module.exports=require(29)
},{}],130:[function(require,module,exports){
module.exports=require(14)
},{}],131:[function(require,module,exports){
module.exports=require(57)
},{}],132:[function(require,module,exports){
module.exports=require(15)
},{}],133:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":130,"./lib/window.console.js":132}],134:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":135,"./lib/timers.js":136}],135:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":124,"polyfill/polyfill-base.js":139}],136:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":139}],137:[function(require,module,exports){
module.exports=require(14)
},{}],138:[function(require,module,exports){
module.exports=require(15)
},{}],139:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":137,"./lib/window.console.js":138}],140:[function(require,module,exports){
module.exports=require(66)
},{}],141:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":140}],142:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":140}],143:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":140}],144:[function(require,module,exports){
module.exports=require(14)
},{}],145:[function(require,module,exports){
module.exports=require(15)
},{}],146:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":144,"./lib/window.console.js":145}],147:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":148,"./lib/timers.js":149}],148:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":124,"polyfill/polyfill-base.js":152}],149:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":152}],150:[function(require,module,exports){
module.exports=require(14)
},{}],151:[function(require,module,exports){
module.exports=require(15)
},{}],152:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":150,"./lib/window.console.js":151}],153:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":154}],154:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":155,"js-ext/lib/object.js":156}],155:[function(require,module,exports){
module.exports=require(4)
},{}],156:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":155,"polyfill/polyfill-base.js":159,"utils":160}],157:[function(require,module,exports){
module.exports=require(14)
},{}],158:[function(require,module,exports){
module.exports=require(15)
},{}],159:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":157,"./lib/window.console.js":158}],160:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":161,"./lib/timers.js":162}],161:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":155,"polyfill/polyfill-base.js":165}],162:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":165}],163:[function(require,module,exports){
module.exports=require(14)
},{}],164:[function(require,module,exports){
module.exports=require(15)
},{}],165:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":163,"./lib/window.console.js":164}],166:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":124,"js-ext/lib/object.js":127,"js-ext/lib/string.js":129,"polyfill":146,"polyfill/extra/transition.js":141,"polyfill/extra/vendorCSS.js":143}],167:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":124,"js-ext/lib/object.js":127,"polyfill":146}],168:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":124,"js-ext/lib/object.js":127,"js-ext/lib/string.js":129,"polyfill":146}],169:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":122,"./attribute-extractor.js":166,"./element-array.js":167,"./html-parser.js":170,"./node-parser.js":171,"./vdom-ns.js":172,"./vnode.js":173,"js-ext/extra/hashmap.js":124,"js-ext/lib/object.js":127,"js-ext/lib/promise.js":128,"js-ext/lib/string.js":129,"polyfill":146,"polyfill/extra/transition.js":141,"polyfill/extra/transitionend.js":142,"polyfill/extra/vendorCSS.js":143,"utils":147,"window-ext":153}],170:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":166,"./vdom-ns.js":172,"js-ext/extra/hashmap.js":124,"js-ext/lib/object.js":127,"polyfill":146}],171:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":166,"./vdom-ns.js":172,"./vnode.js":173,"js-ext/extra/hashmap.js":124,"js-ext/lib/object.js":127,"polyfill":146}],172:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":124,"js-ext/lib/object.js":127,"polyfill":146}],173:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":166,"./html-parser.js":170,"./vdom-ns.js":172,"js-ext/extra/hashmap.js":124,"js-ext/extra/lightmap.js":125,"js-ext/lib/array.js":126,"js-ext/lib/object.js":127,"js-ext/lib/string.js":129,"polyfill":146,"utils/lib/timers.js":149}],174:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":168,"./partials/extend-element.js":169,"./partials/node-parser.js":171,"js-ext/extra/hashmap.js":124,"js-ext/lib/object.js":127,"utils/lib/timers.js":149}],175:[function(require,module,exports){
var css = "*:focus {\n outline: 0;\n}\n\na[target=\"_blank\"]:focus {\n outline: 1px solid #129fea;\n}\n\n/* because we think the padding and margin should always be part of the size,\n we define \"box-sizing: border-box\" for all elements */\n\n* {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}"; (require("/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify"))(css); module.exports = css;
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],176:[function(require,module,exports){
var css = ".pure-menu.pure-menu-open {\n z-index: 3; /* prevent graph from crossing the menuarea */\n}\n\n.pure-button.pure-button-bordered,\n.pure-button.pure-button-bordered[disabled] {\n box-shadow: 0 0 0 1px rgba(0,0,0, 0.15) inset;\n}\n\n.pure-button-active,\n.pure-button:active,\n.pure-button.pure-button-bordered.pure-button-active,\n.pure-button.pure-button-bordered.pure-button-active[disabled],\n.pure-button.pure-button-bordered:active,\n.pure-button.pure-button-bordered[disabled]:active {\n box-shadow: 0 0 0 1px rgba(0,0,0, 0.4) inset, 0 0 6px rgba(0,0,0, 0.2) inset;\n}\n\n.pure-button.pure-button-bordered:focus,\n.pure-button.pure-button-bordered[disabled]:focus,\n.pure-button.pure-button-bordered:focus,\n.pure-button.pure-button-bordered[disabled]:focus,\n.pure-button.pure-button-bordered.focussed,\n.pure-button.pure-button-bordered[disabled].focussed,\n.pure-button.pure-button-bordered.focussed,\n.pure-button.pure-button-bordered[disabled].focussed {\n box-shadow: 0 0 0 1px rgba(0,0,0, 0.6) inset;\n}\n\n/* restore pure-button:active */\n.pure-button.pure-button-bordered:active,\n.pure-button.pure-button-bordered.pure-button-active,\n.pure-button:active:focus,\n.pure-button.pure-button-active:focus {\n box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.6) inset, 0 0 10px rgba(0, 0, 0, 0.2) inset;\n}\n\n.pure-button.pure-button-rounded {\n border-radius: 0.3em;\n}\n\n.pure-button.pure-button-heavyrounded {\n border-radius: 0.5em;\n}\n\n.pure-button.pure-button-oval {\n border-radius: 50%;\n}\n\n.pure-button.pure-button-halfoval {\n border-radius: 25%;\n}\n"; (require("/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify"))(css); module.exports = css;
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],177:[function(require,module,exports){
var css = "/*!\nPure v0.5.0\nCopyright 2014 Yahoo! Inc. All rights reserved.\nLicensed under the BSD License.\nhttps://github.com/yahoo/pure/blob/master/LICENSE.md\n*/\n/*!\nnormalize.css v^3.0 | MIT License | git.io/normalize\nCopyright (c) Nicolas Gallagher and Jonathan Neal\n*/\n/*! normalize.css v3.0.2 | MIT License | git.io/normalize */\n\n/**\n * 1. Set default font family to sans-serif.\n * 2. Prevent iOS text size adjust after orientation change, without disabling\n * user zoom.\n */\n\nhtml {\n font-family: sans-serif; /* 1 */\n -ms-text-size-adjust: 100%; /* 2 */\n -webkit-text-size-adjust: 100%; /* 2 */\n}\n\n/**\n * Remove default margin.\n */\n\nbody {\n margin: 0;\n}\n\n/* HTML5 display definitions\n ========================================================================== */\n\n/**\n * Correct `block` display not defined for any HTML5 element in IE 8/9.\n * Correct `block` display not defined for `details` or `summary` in IE 10/11\n * and Firefox.\n * Correct `block` display not defined for `main` in IE 11.\n */\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n display: block;\n}\n\n/**\n * 1. Correct `inline-block` display not defined in IE 8/9.\n * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.\n */\n\naudio,\ncanvas,\nprogress,\nvideo {\n display: inline-block; /* 1 */\n vertical-align: baseline; /* 2 */\n}\n\n/**\n * Prevent modern browsers from displaying `audio` without controls.\n * Remove excess height in iOS 5 devices.\n */\n\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n\n/**\n * Address `[hidden]` styling not present in IE 8/9/10.\n * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.\n */\n\n[hidden],\ntemplate {\n display: none;\n}\n\n/* Links\n ========================================================================== */\n\n/**\n * Remove the gray background color from active links in IE 10.\n */\n\na {\n background-color: transparent;\n}\n\n/**\n * Improve readability when focused and also mouse hovered in all browsers.\n */\n\na:active,\na:hover {\n outline: 0;\n}\n\n/* Text-level semantics\n ========================================================================== */\n\n/**\n * Address styling not present in IE 8/9/10/11, Safari, and Chrome.\n */\n\nabbr[title] {\n border-bottom: 1px dotted;\n}\n\n/**\n * Address style set to `bolder` in Firefox 4+, Safari, and Chrome.\n */\n\nb,\nstrong {\n font-weight: bold;\n}\n\n/**\n * Address styling not present in Safari and Chrome.\n */\n\ndfn {\n font-style: italic;\n}\n\n/**\n * Address variable `h1` font-size and margin within `section` and `article`\n * contexts in Firefox 4+, Safari, and Chrome.\n */\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n/**\n * Address styling not present in IE 8/9.\n */\n\nmark {\n background: #ff0;\n color: #000;\n}\n\n/**\n * Address inconsistent and variable font size in all browsers.\n */\n\nsmall {\n font-size: 80%;\n}\n\n/**\n * Prevent `sub` and `sup` affecting `line-height` in all browsers.\n */\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsup {\n top: -0.5em;\n}\n\nsub {\n bottom: -0.25em;\n}\n\n/* Embedded content\n ========================================================================== */\n\n/**\n * Remove border when inside `a` element in IE 8/9/10.\n */\n\nimg {\n border: 0;\n}\n\n/**\n * Correct overflow not hidden in IE 9/10/11.\n */\n\nsvg:not(:root) {\n overflow: hidden;\n}\n\n/* Grouping content\n ========================================================================== */\n\n/**\n * Address margin not present in IE 8/9 and Safari.\n */\n\nfigure {\n margin: 1em 40px;\n}\n\n/**\n * Address differences between Firefox and other browsers.\n */\n\nhr {\n -moz-box-sizing: content-box;\n box-sizing: content-box;\n height: 0;\n}\n\n/**\n * Contain overflow in all browsers.\n */\n\npre {\n overflow: auto;\n}\n\n/**\n * Address odd `em`-unit font size rendering in all browsers.\n */\n\ncode,\nkbd,\npre,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\n\n/* Forms\n ========================================================================== */\n\n/**\n * Known limitation: by default, Chrome and Safari on OS X allow very limited\n * styling of `select`, unless a `border` property is set.\n */\n\n/**\n * 1. Correct color not being inherited.\n * Known issue: affects color of disabled elements.\n * 2. Correct font properties not being inherited.\n * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.\n */\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n color: inherit; /* 1 */\n font: inherit; /* 2 */\n margin: 0; /* 3 */\n}\n\n/**\n * Address `overflow` set to `hidden` in IE 8/9/10/11.\n */\n\nbutton {\n overflow: visible;\n}\n\n/**\n * Address inconsistent `text-transform` inheritance for `button` and `select`.\n * All other form control elements do not inherit `text-transform` values.\n * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.\n * Correct `select` style inheritance in Firefox.\n */\n\nbutton,\nselect {\n text-transform: none;\n}\n\n/**\n * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`\n * and `video` controls.\n * 2. Correct inability to style clickable `input` types in iOS.\n * 3. Improve usability and consistency of cursor style between image-type\n * `input` and others.\n */\n\nbutton,\nhtml input[type=\"button\"], /* 1 */\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n -webkit-appearance: button; /* 2 */\n cursor: pointer; /* 3 */\n}\n\n/**\n * Re-set default cursor for disabled elements.\n */\n\nbutton[disabled],\nhtml input[disabled] {\n cursor: default;\n}\n\n/**\n * Remove inner padding and border in Firefox 4+.\n */\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\n\n/**\n * Address Firefox 4+ setting `line-height` on `input` using `!important` in\n * the UA stylesheet.\n */\n\ninput {\n line-height: normal;\n}\n\n/**\n * It's recommended that you don't attempt to style these elements.\n * Firefox's implementation doesn't respect box-sizing, padding, or width.\n *\n * 1. Address box sizing set to `content-box` in IE 8/9/10.\n * 2. Remove excess padding in IE 8/9/10.\n */\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n box-sizing: border-box; /* 1 */\n padding: 0; /* 2 */\n}\n\n/**\n * Fix the cursor style for Chrome's increment/decrement buttons. For certain\n * `font-size` values of the `input`, it causes the cursor style of the\n * decrement button to change from `default` to `text`.\n */\n\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n/**\n * 1. Address `appearance` set to `searchfield` in Safari and Chrome.\n * 2. Address `box-sizing` set to `border-box` in Safari and Chrome\n * (include `-moz` to future-proof).\n */\n\ninput[type=\"search\"] {\n -webkit-appearance: textfield; /* 1 */\n -moz-box-sizing: content-box;\n -webkit-box-sizing: content-box; /* 2 */\n box-sizing: content-box;\n}\n\n/**\n * Remove inner padding and search cancel button in Safari and Chrome on OS X.\n * Safari (but not Chrome) clips the cancel button when the search input has\n * padding (and `textfield` appearance).\n */\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n/**\n * Define consistent border, margin, and padding.\n */\n\nfieldset {\n border: 1px solid #c0c0c0;\n margin: 0 2px;\n padding: 0.35em 0.625em 0.75em;\n}\n\n/**\n * 1. Correct `color` not being inherited in IE 8/9/10/11.\n * 2. Remove padding so people aren't caught out if they zero out fieldsets.\n */\n\nlegend {\n border: 0; /* 1 */\n padding: 0; /* 2 */\n}\n\n/**\n * Remove default vertical scrollbar in IE 8/9/10/11.\n */\n\ntextarea {\n overflow: auto;\n}\n\n/**\n * Don't inherit the `font-weight` (applied by a rule above).\n * NOTE: the default cannot safely be changed in Chrome and Safari on OS X.\n */\n\noptgroup {\n font-weight: bold;\n}\n\n/* Tables\n ========================================================================== */\n\n/**\n * Remove most spacing between table cells.\n */\n\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\n\ntd,\nth {\n padding: 0;\n}\n\n/*csslint important:false*/\n\n/* ==========================================================================\n Pure Base Extras\n ========================================================================== */\n\n/**\n * Extra rules that Pure adds on top of Normalize.css\n */\n\n/**\n * Always hide an element when it has the `hidden` HTML attribute.\n */\n\n[hidden] {\n display: none !important;\n}\n\n/**\n * Add this class to an image to make it fit within it's fluid parent wrapper while maintaining\n * aspect ratio.\n */\n.pure-img {\n max-width: 100%;\n height: auto;\n display: block;\n}\n\n/*csslint regex-selectors:false, known-properties:false, duplicate-properties:false*/\n\n.pure-g {\n letter-spacing: -0.31em; /* Webkit: collapse white-space between units */\n *letter-spacing: normal; /* reset IE < 8 */\n *word-spacing: -0.43em; /* IE < 8: collapse white-space between units */\n text-rendering: optimizespeed; /* Webkit: fixes text-rendering: optimizeLegibility */\n\n /*\n Sets the font stack to fonts known to work properly with the above letter\n and word spacings. See: https://github.com/yahoo/pure/issues/41/\n\n The following font stack makes Pure Grids work on all known environments.\n\n * FreeSans: Ships with many Linux distros, including Ubuntu\n\n * Arimo: Ships with Chrome OS. Arimo has to be defined before Helvetica and\n Arial to get picked up by the browser, even though neither is available\n in Chrome OS.\n\n * Droid Sans: Ships with all versions of Android.\n\n * Helvetica, Arial, sans-serif: Common font stack on OS X and Windows.\n */\n font-family: FreeSans, Arimo, \"Droid Sans\", Helvetica, Arial, sans-serif;\n\n /*\n Use flexbox when possible to avoid `letter-spacing` side-effects.\n\n NOTE: Firefox (as of 25) does not currently support flex-wrap, so the\n `-moz-` prefix version is omitted.\n */\n\n display: -webkit-flex;\n -webkit-flex-flow: row wrap;\n\n /* IE10 uses display: flexbox */\n display: -ms-flexbox;\n -ms-flex-flow: row wrap;\n}\n\n/* Opera as of 12 on Windows needs word-spacing.\n The \".opera-only\" selector is used to prevent actual prefocus styling\n and is not required in markup.\n*/\n.opera-only :-o-prefocus,\n.pure-g {\n word-spacing: -0.43em;\n}\n\n.pure-u {\n display: inline-block;\n *display: inline; /* IE < 8: fake inline-block */\n zoom: 1;\n letter-spacing: normal;\n word-spacing: normal;\n vertical-align: top;\n text-rendering: auto;\n}\n\n/*\nResets the font family back to the OS/browser's default sans-serif font,\nthis the same font stack that Normalize.css sets for the `body`.\n*/\n.pure-g [class *= \"pure-u\"] {\n font-family: sans-serif;\n}\n\n.pure-u-1,\n.pure-u-1-1,\n.pure-u-1-2,\n.pure-u-1-3,\n.pure-u-2-3,\n.pure-u-1-4,\n.pure-u-3-4,\n.pure-u-1-5,\n.pure-u-2-5,\n.pure-u-3-5,\n.pure-u-4-5,\n.pure-u-5-5,\n.pure-u-1-6,\n.pure-u-5-6,\n.pure-u-1-8,\n.pure-u-3-8,\n.pure-u-5-8,\n.pure-u-7-8,\n.pure-u-1-12,\n.pure-u-5-12,\n.pure-u-7-12,\n.pure-u-11-12,\n.pure-u-1-24,\n.pure-u-2-24,\n.pure-u-3-24,\n.pure-u-4-24,\n.pure-u-5-24,\n.pure-u-6-24,\n.pure-u-7-24,\n.pure-u-8-24,\n.pure-u-9-24,\n.pure-u-10-24,\n.pure-u-11-24,\n.pure-u-12-24,\n.pure-u-13-24,\n.pure-u-14-24,\n.pure-u-15-24,\n.pure-u-16-24,\n.pure-u-17-24,\n.pure-u-18-24,\n.pure-u-19-24,\n.pure-u-20-24,\n.pure-u-21-24,\n.pure-u-22-24,\n.pure-u-23-24,\n.pure-u-24-24 {\n display: inline-block;\n *display: inline;\n zoom: 1;\n letter-spacing: normal;\n word-spacing: normal;\n vertical-align: top;\n text-rendering: auto;\n}\n\n.pure-u-1-24 {\n width: 4.1667%;\n *width: 4.1357%;\n}\n\n.pure-u-1-12,\n.pure-u-2-24 {\n width: 8.3333%;\n *width: 8.3023%;\n}\n\n.pure-u-1-8,\n.pure-u-3-24 {\n width: 12.5000%;\n *width: 12.4690%;\n}\n\n.pure-u-1-6,\n.pure-u-4-24 {\n width: 16.6667%;\n *width: 16.6357%;\n}\n\n.pure-u-1-5 {\n width: 20%;\n *width: 19.9690%;\n}\n\n.pure-u-5-24 {\n width: 20.8333%;\n *width: 20.8023%;\n}\n\n.pure-u-1-4,\n.pure-u-6-24 {\n width: 25%;\n *width: 24.9690%;\n}\n\n.pure-u-7-24 {\n width: 29.1667%;\n *width: 29.1357%;\n}\n\n.pure-u-1-3,\n.pure-u-8-24 {\n width: 33.3333%;\n *width: 33.3023%;\n}\n\n.pure-u-3-8,\n.pure-u-9-24 {\n width: 37.5000%;\n *width: 37.4690%;\n}\n\n.pure-u-2-5 {\n width: 40%;\n *width: 39.9690%;\n}\n\n.pure-u-5-12,\n.pure-u-10-24 {\n width: 41.6667%;\n *width: 41.6357%;\n}\n\n.pure-u-11-24 {\n width: 45.8333%;\n *width: 45.8023%;\n}\n\n.pure-u-1-2,\n.pure-u-12-24 {\n width: 50%;\n *width: 49.9690%;\n}\n\n.pure-u-13-24 {\n width: 54.1667%;\n *width: 54.1357%;\n}\n\n.pure-u-7-12,\n.pure-u-14-24 {\n width: 58.3333%;\n *width: 58.3023%;\n}\n\n.pure-u-3-5 {\n width: 60%;\n *width: 59.9690%;\n}\n\n.pure-u-5-8,\n.pure-u-15-24 {\n width: 62.5000%;\n *width: 62.4690%;\n}\n\n.pure-u-2-3,\n.pure-u-16-24 {\n width: 66.6667%;\n *width: 66.6357%;\n}\n\n.pure-u-17-24 {\n width: 70.8333%;\n *width: 70.8023%;\n}\n\n.pure-u-3-4,\n.pure-u-18-24 {\n width: 75%;\n *width: 74.9690%;\n}\n\n.pure-u-19-24 {\n width: 79.1667%;\n *width: 79.1357%;\n}\n\n.pure-u-4-5 {\n width: 80%;\n *width: 79.9690%;\n}\n\n.pure-u-5-6,\n.pure-u-20-24 {\n width: 83.3333%;\n *width: 83.3023%;\n}\n\n.pure-u-7-8,\n.pure-u-21-24 {\n width: 87.5000%;\n *width: 87.4690%;\n}\n\n.pure-u-11-12,\n.pure-u-22-24 {\n width: 91.6667%;\n *width: 91.6357%;\n}\n\n.pure-u-23-24 {\n width: 95.8333%;\n *width: 95.8023%;\n}\n\n.pure-u-1,\n.pure-u-1-1,\n.pure-u-5-5,\n.pure-u-24-24 {\n width: 100%;\n}\n.pure-button {\n /* Structure */\n display: inline-block;\n *display: inline; /*IE 6/7*/\n zoom: 1;\n line-height: normal;\n white-space: nowrap;\n vertical-align: baseline;\n text-align: center;\n cursor: pointer;\n -webkit-user-drag: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n}\n\n/* Firefox: Get rid of the inner focus border */\n.pure-button::-moz-focus-inner {\n padding: 0;\n border: 0;\n}\n\n/*csslint outline-none:false*/\n\n.pure-button {\n font-family: inherit;\n font-size: 100%;\n *font-size: 90%; /*IE 6/7 - To reduce IE's oversized button text*/\n *overflow: visible; /*IE 6/7 - Because of IE's overly large left/right padding on buttons */\n padding: 0.5em 1em;\n color: #444; /* rgba not supported (IE 8) */\n color: rgba(0, 0, 0, 0.80); /* rgba supported */\n *color: #444; /* IE 6 & 7 */\n border: 1px solid #999; /*IE 6/7/8*/\n border: none rgba(0, 0, 0, 0); /*IE9 + everything else*/\n background-color: #E6E6E6;\n text-decoration: none;\n border-radius: 2px;\n}\n\n.pure-button-hover,\n.pure-button:hover,\n.pure-button:focus {\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#1a000000',GradientType=0);\n background-image: -webkit-gradient(linear, 0 0, 0 100%, from(transparent), color-stop(40%, rgba(0,0,0, 0.05)), to(rgba(0,0,0, 0.10)));\n background-image: -webkit-linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10));\n background-image: -moz-linear-gradient(top, rgba(0,0,0, 0.05) 0%, rgba(0,0,0, 0.10));\n background-image: -o-linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10));\n background-image: linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10));\n}\n.pure-button:focus {\n outline: 0;\n}\n.pure-button-active,\n.pure-button:active {\n box-shadow: 0 0 0 1px rgba(0,0,0, 0.15) inset, 0 0 6px rgba(0,0,0, 0.20) inset;\n}\n\n.pure-button[disabled],\n.pure-button-disabled,\n.pure-button-disabled:hover,\n.pure-button-disabled:focus,\n.pure-button-disabled:active {\n border: none;\n background-image: none;\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n filter: alpha(opacity=40);\n -khtml-opacity: 0.40;\n -moz-opacity: 0.40;\n opacity: 0.40;\n cursor: not-allowed;\n box-shadow: none;\n}\n\n.pure-button-hidden {\n display: none;\n}\n\n/* Firefox: Get rid of the inner focus border */\n.pure-button::-moz-focus-inner{\n padding: 0;\n border: 0;\n}\n\n.pure-button-primary,\n.pure-button-selected,\na.pure-button-primary,\na.pure-button-selected {\n background-color: rgb(0, 120, 231);\n color: #fff;\n}\n\n.pure-form input[type=\"text\"],\n.pure-form input[type=\"password\"],\n.pure-form input[type=\"email\"],\n.pure-form input[type=\"url\"],\n.pure-form input[type=\"date\"],\n.pure-form input[type=\"month\"],\n.pure-form input[type=\"time\"],\n.pure-form input[type=\"datetime\"],\n.pure-form input[type=\"datetime-local\"],\n.pure-form input[type=\"week\"],\n.pure-form input[type=\"number\"],\n.pure-form input[type=\"search\"],\n.pure-form input[type=\"tel\"],\n.pure-form input[type=\"color\"],\n.pure-form select,\n.pure-form textarea {\n padding: 0.5em 0.6em;\n display: inline-block;\n border: 1px solid #ccc;\n box-shadow: inset 0 1px 3px #ddd;\n border-radius: 4px;\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n\n/*\nNeed to separate out the :not() selector from the rest of the CSS 2.1 selectors\nsince IE8 won't execute CSS that contains a CSS3 selector.\n*/\n.pure-form input:not([type]) {\n padding: 0.5em 0.6em;\n display: inline-block;\n border: 1px solid #ccc;\n box-shadow: inset 0 1px 3px #ddd;\n border-radius: 4px;\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n\n\n/* Chrome (as of v.32/34 on OS X) needs additional room for color to display. */\n/* May be able to remove this tweak as color inputs become more standardized across browsers. */\n.pure-form input[type=\"color\"] {\n padding: 0.2em 0.5em;\n}\n\n\n.pure-form input[type=\"text\"]:focus,\n.pure-form input[type=\"password\"]:focus,\n.pure-form input[type=\"email\"]:focus,\n.pure-form input[type=\"url\"]:focus,\n.pure-form input[type=\"date\"]:focus,\n.pure-form input[type=\"month\"]:focus,\n.pure-form input[type=\"time\"]:focus,\n.pure-form input[type=\"datetime\"]:focus,\n.pure-form input[type=\"datetime-local\"]:focus,\n.pure-form input[type=\"week\"]:focus,\n.pure-form input[type=\"number\"]:focus,\n.pure-form input[type=\"search\"]:focus,\n.pure-form input[type=\"tel\"]:focus,\n.pure-form input[type=\"color\"]:focus,\n.pure-form select:focus,\n.pure-form textarea:focus {\n outline: 0;\n outline: thin dotted \\9; /* IE6-9 */\n border-color: #129FEA;\n}\n\n/*\nNeed to separate out the :not() selector from the rest of the CSS 2.1 selectors\nsince IE8 won't execute CSS that contains a CSS3 selector.\n*/\n.pure-form input:not([type]):focus {\n outline: 0;\n outline: thin dotted \\9; /* IE6-9 */\n border-color: #129FEA;\n}\n\n.pure-form input[type=\"file\"]:focus,\n.pure-form input[type=\"radio\"]:focus,\n.pure-form input[type=\"checkbox\"]:focus {\n outline: thin dotted #333;\n outline: 1px auto #129FEA;\n}\n.pure-form .pure-checkbox,\n.pure-form .pure-radio {\n margin: 0.5em 0;\n display: block;\n}\n\n.pure-form input[type=\"text\"][disabled],\n.pure-form input[type=\"password\"][disabled],\n.pure-form input[type=\"email\"][disabled],\n.pure-form input[type=\"url\"][disabled],\n.pure-form input[type=\"date\"][disabled],\n.pure-form input[type=\"month\"][disabled],\n.pure-form input[type=\"time\"][disabled],\n.pure-form input[type=\"datetime\"][disabled],\n.pure-form input[type=\"datetime-local\"][disabled],\n.pure-form input[type=\"week\"][disabled],\n.pure-form input[type=\"number\"][disabled],\n.pure-form input[type=\"search\"][disabled],\n.pure-form input[type=\"tel\"][disabled],\n.pure-form input[type=\"color\"][disabled],\n.pure-form select[disabled],\n.pure-form textarea[disabled] {\n cursor: not-allowed;\n background-color: #eaeded;\n color: #cad2d3;\n}\n\n/*\nNeed to separate out the :not() selector from the rest of the CSS 2.1 selectors\nsince IE8 won't execute CSS that contains a CSS3 selector.\n*/\n.pure-form input:not([type])[disabled] {\n cursor: not-allowed;\n background-color: #eaeded;\n color: #cad2d3;\n}\n.pure-form input[readonly],\n.pure-form select[readonly],\n.pure-form textarea[readonly] {\n background: #eee; /* menu hover bg color */\n color: #777; /* menu text color */\n border-color: #ccc;\n}\n\n.pure-form input:focus:invalid,\n.pure-form textarea:focus:invalid,\n.pure-form select:focus:invalid {\n color: #b94a48;\n border-color: #ee5f5b;\n}\n.pure-form input:focus:invalid:focus,\n.pure-form textarea:focus:invalid:focus,\n.pure-form select:focus:invalid:focus {\n border-color: #e9322d;\n}\n.pure-form input[type=\"file\"]:focus:invalid:focus,\n.pure-form input[type=\"radio\"]:focus:invalid:focus,\n.pure-form input[type=\"checkbox\"]:focus:invalid:focus {\n outline-color: #e9322d;\n}\n.pure-form select {\n border: 1px solid #ccc;\n background-color: white;\n}\n.pure-form select[multiple] {\n height: auto;\n}\n.pure-form label {\n margin: 0.5em 0 0.2em;\n}\n.pure-form fieldset {\n margin: 0;\n padding: 0.35em 0 0.75em;\n border: 0;\n}\n.pure-form legend {\n display: block;\n width: 100%;\n padding: 0.3em 0;\n margin-bottom: 0.3em;\n color: #333;\n border-bottom: 1px solid #e5e5e5;\n}\n\n.pure-form-stacked input[type=\"text\"],\n.pure-form-stacked input[type=\"password\"],\n.pure-form-stacked input[type=\"email\"],\n.pure-form-stacked input[type=\"url\"],\n.pure-form-stacked input[type=\"date\"],\n.pure-form-stacked input[type=\"month\"],\n.pure-form-stacked input[type=\"time\"],\n.pure-form-stacked input[type=\"datetime\"],\n.pure-form-stacked input[type=\"datetime-local\"],\n.pure-form-stacked input[type=\"week\"],\n.pure-form-stacked input[type=\"number\"],\n.pure-form-stacked input[type=\"search\"],\n.pure-form-stacked input[type=\"tel\"],\n.pure-form-stacked input[type=\"color\"],\n.pure-form-stacked select,\n.pure-form-stacked label,\n.pure-form-stacked textarea {\n display: block;\n margin: 0.25em 0;\n}\n\n/*\nNeed to separate out the :not() selector from the rest of the CSS 2.1 selectors\nsince IE8 won't execute CSS that contains a CSS3 selector.\n*/\n.pure-form-stacked input:not([type]) {\n display: block;\n margin: 0.25em 0;\n}\n.pure-form-aligned input,\n.pure-form-aligned textarea,\n.pure-form-aligned select,\n/* NOTE: pure-help-inline is deprecated. Use .pure-form-message-inline instead. */\n.pure-form-aligned .pure-help-inline,\n.pure-form-message-inline {\n display: inline-block;\n *display: inline;\n *zoom: 1;\n vertical-align: middle;\n}\n.pure-form-aligned textarea {\n vertical-align: top;\n}\n\n/* Aligned Forms */\n.pure-form-aligned .pure-control-group {\n margin-bottom: 0.5em;\n}\n.pure-form-aligned .pure-control-group label {\n text-align: right;\n display: inline-block;\n vertical-align: middle;\n width: 10em;\n margin: 0 1em 0 0;\n}\n.pure-form-aligned .pure-controls {\n margin: 1.5em 0 0 10em;\n}\n\n/* Rounded Inputs */\n.pure-form input.pure-input-rounded,\n.pure-form .pure-input-rounded {\n border-radius: 2em;\n padding: 0.5em 1em;\n}\n\n/* Grouped Inputs */\n.pure-form .pure-group fieldset {\n margin-bottom: 10px;\n}\n.pure-form .pure-group input {\n display: block;\n padding: 10px;\n margin: 0;\n border-radius: 0;\n position: relative;\n top: -1px;\n}\n.pure-form .pure-group input:focus {\n z-index: 2;\n}\n.pure-form .pure-group input:first-child {\n top: 1px;\n border-radius: 4px 4px 0 0;\n}\n.pure-form .pure-group input:last-child {\n top: -2px;\n border-radius: 0 0 4px 4px;\n}\n.pure-form .pure-group button {\n margin: 0.35em 0;\n}\n\n.pure-form .pure-input-1 {\n width: 100%;\n}\n.pure-form .pure-input-2-3 {\n width: 66%;\n}\n.pure-form .pure-input-1-2 {\n width: 50%;\n}\n.pure-form .pure-input-1-3 {\n width: 33%;\n}\n.pure-form .pure-input-1-4 {\n width: 25%;\n}\n\n/* Inline help for forms */\n/* NOTE: pure-help-inline is deprecated. Use .pure-form-message-inline instead. */\n.pure-form .pure-help-inline,\n.pure-form-message-inline {\n display: inline-block;\n padding-left: 0.3em;\n color: #666;\n vertical-align: middle;\n font-size: 0.875em;\n}\n\n/* Block help for forms */\n.pure-form-message {\n display: block;\n color: #666;\n font-size: 0.875em;\n}\n\n@media only screen and (max-width : 480px) {\n .pure-form button[type=\"submit\"] {\n margin: 0.7em 0 0;\n }\n\n .pure-form input:not([type]),\n .pure-form input[type=\"text\"],\n .pure-form input[type=\"password\"],\n .pure-form input[type=\"email\"],\n .pure-form input[type=\"url\"],\n .pure-form input[type=\"date\"],\n .pure-form input[type=\"month\"],\n .pure-form input[type=\"time\"],\n .pure-form input[type=\"datetime\"],\n .pure-form input[type=\"datetime-local\"],\n .pure-form input[type=\"week\"],\n .pure-form input[type=\"number\"],\n .pure-form input[type=\"search\"],\n .pure-form input[type=\"tel\"],\n .pure-form input[type=\"color\"],\n .pure-form label {\n margin-bottom: 0.3em;\n display: block;\n }\n\n .pure-group input:not([type]),\n .pure-group input[type=\"text\"],\n .pure-group input[type=\"password\"],\n .pure-group input[type=\"email\"],\n .pure-group input[type=\"url\"],\n .pure-group input[type=\"date\"],\n .pure-group input[type=\"month\"],\n .pure-group input[type=\"time\"],\n .pure-group input[type=\"datetime\"],\n .pure-group input[type=\"datetime-local\"],\n .pure-group input[type=\"week\"],\n .pure-group input[type=\"number\"],\n .pure-group input[type=\"search\"],\n .pure-group input[type=\"tel\"],\n .pure-group input[type=\"color\"] {\n margin-bottom: 0;\n }\n\n .pure-form-aligned .pure-control-group label {\n margin-bottom: 0.3em;\n text-align: left;\n display: block;\n width: 100%;\n }\n\n .pure-form-aligned .pure-controls {\n margin: 1.5em 0 0 0;\n }\n\n /* NOTE: pure-help-inline is deprecated. Use .pure-form-message-inline instead. */\n .pure-form .pure-help-inline,\n .pure-form-message-inline,\n .pure-form-message {\n display: block;\n font-size: 0.75em;\n /* Increased bottom padding to make it group with its related input element. */\n padding: 0.2em 0 0.8em;\n }\n}\n\n/*csslint adjoining-classes:false, outline-none:false*/\n/*TODO: Remove this lint rule override after a refactor of this code.*/\n\n.pure-menu ul {\n position: absolute;\n visibility: hidden;\n}\n\n.pure-menu.pure-menu-open {\n visibility: visible;\n z-index: 2;\n width: 100%;\n}\n\n.pure-menu ul {\n left: -10000px;\n list-style: none;\n margin: 0;\n padding: 0;\n top: -10000px;\n z-index: 1;\n}\n\n.pure-menu > ul { position: relative; }\n\n.pure-menu-open > ul {\n left: 0;\n top: 0;\n visibility: visible;\n}\n\n.pure-menu-open > ul:focus {\n outline: 0;\n}\n\n.pure-menu li { position: relative; }\n\n.pure-menu a,\n.pure-menu .pure-menu-heading {\n display: block;\n color: inherit;\n line-height: 1.5em;\n padding: 5px 20px;\n text-decoration: none;\n white-space: nowrap;\n}\n\n.pure-menu.pure-menu-horizontal > .pure-menu-heading {\n display: inline-block;\n *display: inline;\n zoom: 1;\n margin: 0;\n vertical-align: middle;\n}\n.pure-menu.pure-menu-horizontal > ul {\n display: inline-block;\n *display: inline;\n zoom: 1;\n vertical-align: middle;\n}\n\n.pure-menu li a { padding: 5px 20px; }\n\n.pure-menu-can-have-children > .pure-menu-label:after {\n content: '\\25B8';\n float: right;\n /* These specific fonts have the Unicode char we need. */\n font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'DejaVu Sans', sans-serif;\n margin-right: -20px;\n margin-top: -1px;\n}\n\n.pure-menu-can-have-children > .pure-menu-label {\n padding-right: 30px;\n}\n\n.pure-menu-separator {\n background-color: #dfdfdf;\n display: block;\n height: 1px;\n font-size: 0;\n margin: 7px 2px;\n overflow: hidden;\n}\n\n.pure-menu-hidden {\n display: none;\n}\n\n/* FIXED MENU */\n.pure-menu-fixed {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n}\n\n\n/* HORIZONTAL MENU CODE */\n\n/* Initial menus should be inline-block so that they are horizontal */\n.pure-menu-horizontal li {\n display: inline-block;\n *display: inline;\n zoom: 1;\n vertical-align: middle;\n}\n\n/* Submenus should still be display: block; */\n.pure-menu-horizontal li li {\n display: block;\n}\n\n/* Content after should be down arrow */\n.pure-menu-horizontal > .pure-menu-children > .pure-menu-can-have-children > .pure-menu-label:after {\n content: \"\\25BE\";\n}\n/*Add extra padding to elements that have the arrow so that the hover looks nice */\n.pure-menu-horizontal > .pure-menu-children > .pure-menu-can-have-children > .pure-menu-label {\n padding-right: 30px;\n}\n\n/* Adjusting separator for vertical menus */\n.pure-menu-horizontal li.pure-menu-separator {\n height: 50%;\n width: 1px;\n margin: 0 7px;\n}\n\n/* Submenus should be horizontal separator again */\n.pure-menu-horizontal li li.pure-menu-separator {\n height: 1px;\n width: auto;\n margin: 7px 2px;\n}\n\n\n/*csslint adjoining-classes:false*/\n/*TODO: Remove this lint rule override after a refactor of this code.*/\n\n/* MAIN MENU STYLING */\n\n.pure-menu.pure-menu-open,\n.pure-menu.pure-menu-horizontal li .pure-menu-children {\n background: #fff; /* Old browsers */\n border: 1px solid #b7b7b7;\n}\n\n/* remove borders for horizontal menus */\n.pure-menu.pure-menu-horizontal,\n.pure-menu.pure-menu-horizontal .pure-menu-heading {\n border: none;\n}\n\n\n/* LINK STYLES */\n\n.pure-menu a {\n border: 1px solid transparent;\n border-left: none;\n border-right: none;\n\n}\n\n.pure-menu a,\n.pure-menu .pure-menu-can-have-children > li:after {\n color: #777;\n}\n\n.pure-menu .pure-menu-can-have-children > li:hover:after {\n color: #fff;\n}\n\n/* Focus style for a dropdown menu-item when the parent has been opened */\n.pure-menu .pure-menu-open {\n background: #dedede;\n}\n\n\n.pure-menu li a:hover,\n.pure-menu li a:focus {\n background: #eee;\n}\n\n/* DISABLED STATES */\n.pure-menu li.pure-menu-disabled a:hover,\n.pure-menu li.pure-menu-disabled a:focus {\n background: #fff;\n color: #bfbfbf;\n}\n\n.pure-menu .pure-menu-disabled > a {\n background-image: none;\n border-color: transparent;\n cursor: default;\n}\n\n.pure-menu .pure-menu-disabled > a,\n.pure-menu .pure-menu-can-have-children.pure-menu-disabled > a:after {\n color: #bfbfbf;\n}\n\n/* HEADINGS */\n.pure-menu .pure-menu-heading {\n color: #565d64;\n text-transform: uppercase;\n font-size: 90%;\n margin-top: 0.5em;\n border-bottom-width: 1px;\n border-bottom-style: solid;\n border-bottom-color: #dfdfdf;\n}\n\n/* ACTIVE MENU ITEM */\n.pure-menu .pure-menu-selected a {\n color: #000;\n}\n\n/* FIXED MENU */\n.pure-menu.pure-menu-open.pure-menu-fixed {\n border: none;\n border-bottom: 1px solid #b7b7b7;\n}\n\n/*csslint box-model:false*/\n/*TODO: Remove this lint rule override after a refactor of this code.*/\n\n\n.pure-paginator {\n\n /* `pure-g` Grid styles */\n letter-spacing: -0.31em; /* Webkit: collapse white-space between units */\n *letter-spacing: normal; /* reset IE < 8 */\n *word-spacing: -0.43em; /* IE < 8: collapse white-space between units */\n text-rendering: optimizespeed; /* Webkit: fixes text-rendering: optimizeLegibility */\n\n /* `pure-paginator` Specific styles */\n list-style: none;\n margin: 0;\n padding: 0;\n}\n.opera-only :-o-prefocus,\n.pure-paginator {\n word-spacing: -0.43em;\n}\n\n/* `pure-u` Grid styles */\n.pure-paginator li {\n display: inline-block;\n *display: inline; /* IE < 8: fake inline-block */\n zoom: 1;\n letter-spacing: normal;\n word-spacing: normal;\n vertical-align: top;\n text-rendering: auto;\n}\n\n\n.pure-paginator .pure-button {\n border-radius: 0;\n padding: 0.8em 1.4em;\n vertical-align: top;\n height: 1.1em;\n}\n.pure-paginator .pure-button:focus,\n.pure-paginator .pure-button:active {\n outline-style: none;\n}\n.pure-paginator .prev,\n.pure-paginator .next {\n color: #C0C1C3;\n text-shadow: 0 -1px 0 rgba(0,0,0, 0.45);\n}\n.pure-paginator .prev {\n border-radius: 2px 0 0 2px;\n}\n.pure-paginator .next {\n border-radius: 0 2px 2px 0;\n}\n\n@media (max-width: 480px) {\n .pure-menu-horizontal {\n width: 100%;\n }\n\n .pure-menu-children li {\n display: block;\n border-bottom: 1px solid black;\n }\n}\n\n.pure-table {\n /* Remove spacing between table cells (from Normalize.css) */\n border-collapse: collapse;\n border-spacing: 0;\n empty-cells: show;\n border: 1px solid #cbcbcb;\n}\n\n.pure-table caption {\n color: #000;\n font: italic 85%/1 arial, sans-serif;\n padding: 1em 0;\n text-align: center;\n}\n\n.pure-table td,\n.pure-table th {\n border-left: 1px solid #cbcbcb;/* inner column border */\n border-width: 0 0 0 1px;\n font-size: inherit;\n margin: 0;\n overflow: visible; /*to make ths where the title is really long work*/\n padding: 0.5em 1em; /* cell padding */\n}\n.pure-table td:first-child,\n.pure-table th:first-child {\n border-left-width: 0;\n}\n\n.pure-table thead {\n background: #e0e0e0;\n color: #000;\n text-align: left;\n vertical-align: bottom;\n}\n\n/*\nstriping:\n even - #fff (white)\n odd - #f2f2f2 (light gray)\n*/\n.pure-table td {\n background-color: transparent;\n}\n.pure-table-odd td {\n background-color: #f2f2f2;\n}\n\n/* nth-child selector for modern browsers */\n.pure-table-striped tr:nth-child(2n-1) td {\n background-color: #f2f2f2;\n}\n\n/* BORDERED TABLES */\n.pure-table-bordered td {\n border-bottom: 1px solid #cbcbcb;\n}\n.pure-table-bordered tbody > tr:last-child > td {\n border-bottom-width: 0;\n}\n\n\n/* HORIZONTAL BORDERED TABLES */\n\n.pure-table-horizontal td,\n.pure-table-horizontal th {\n border-width: 0 0 1px 0;\n border-bottom: 1px solid #cbcbcb;\n}\n.pure-table-horizontal tbody > tr:last-child > td {\n border-bottom-width: 0;\n}\n"; (require("/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify"))(css); module.exports = css;
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],178:[function(require,module,exports){
require('./css/default.css');
require('./css/purecss-0.5.0.css');
require('./css/pure-finetuned.css');
},{"./css/default.css":175,"./css/pure-finetuned.css":176,"./css/purecss-0.5.0.css":177}],179:[function(require,module,exports){
var css = "[plugin-panel=\"true\"] div.message {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n padding: 0;\n margin: 0 0 1.8em;\n}\n\n[plugin-panel=\"true\"] label[for=\"iprompt\"] {\n margin-right: 1em;\n}"; (require("/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify"))(css); module.exports = css;
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],180:[function(require,module,exports){
"use strict";
/**
* Creating floating Panel-nodes which can be shown and hidden.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
*
* @module panel
* @class Panel
* @since 0.0.1
*/
require('js-ext');
require('messages');
require('polyfill');
require('./css/dialog.css');
var NAME = '[dialog]: ',
MESSAGE_LEVELS = {
'message': 1,
'warning': 2,
'error': 3
},
MESSAGE_HASHES = {
'message': 'messages',
'warning': 'warnings',
'error': 'errors'
},
MESSAGE_HASHES_NR = {
1: 'messages',
2: 'warnings',
3: 'errors'
},
FOLLOWUP_DELAY = 200,
createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
var DOCUMENT = window.document,
Classes = require('js-ext/extra/classes.js'),
UTILS = require('utils'),
later = UTILS.later,
async = UTILS.async,
dialog, Dialog, Event;
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
/*jshint boss:true */
if (Dialog=window._ITSAmodules.Dialog) {
/*jshint boss:false */
return Dialog; // Dialog was already created
}
require('panel')(window);
Event = require('event');
Dialog = Classes.createClass(function() {
var instance = this;
instance.model = {
draggable: true
};
instance.currentMessageLevel = 0;
instance.messages = [];
instance.warnings = [];
instance.errors = [];
instance.createContainer();
instance.setupListeners();
}, {
createContainer: function() {
var instance = this,
model = instance.model;
model.callback = function(buttonNode) {
var containerNode = DOCUMENT.createElement('div'),
contentNode = instance.panel.getElement('>div[is="content"]'),
messagePromise = model.messagePromise,
node;
// move all childNodes from contentNode inside the new DIV
// we need to start with position 2 --> the first 2 nodes are the scroller-nodes
/*jshint boss:true */
while (node=contentNode.childNodes[2]) {
/*jshint boss:false */
containerNode.appendChild(node);
}
// now append a copy of the buttonNode:
containerNode.append(buttonNode.getOuterHTML());
messagePromise.fulfill(containerNode);
// we can safely remove the newly created container-node: the vdom holds it for 1 minute
containerNode.remove();
};
instance.panel = DOCUMENT.createPanel(model);
},
processMessage: function(e) {
var instance = this,
messagePromise = e.messagePromise,
type = e.type,
level = MESSAGE_LEVELS[type],
list = instance[MESSAGE_HASHES[type]];
list[list.length] = messagePromise;
messagePromise.finally(
function() {
list.remove(messagePromise);
// handle the next message (if there)
instance.handleMessage(true);
}
);
(level>instance.currentMessageLevel) && instance.handleMessage(!instance.isWaiting(), level);
},
setupListeners: function() {
var instance = this;
Event.after(['*:message', '*:warning', '*:error'], instance.processMessage.bind(instance));
},
isWaiting: function() {
return (this.currentMessageLevel===0);
},
handleMessage: function(delay, level) {
var instance = this,
model = instance.model,
messagePromise;
if (!level) {
// search level
if (instance.errors.length>0) {
level = 3;
}
else if (instance.warnings.length>0) {
level = 2;
}
else if (instance.messages.length>0) {
level = 1;
}
}
if (!level || (instance[MESSAGE_HASHES_NR[level]].length===0)) {
// DO NOT make messagePromise null: it sould be there as return value
// of the last message
instance.currentMessageLevel = 0;
model.header = null;
model.content = '';
model.footer = null;
model.visible = false;
return;
}
instance.currentMessageLevel = level;
// now process the highest message
messagePromise = instance[MESSAGE_HASHES_NR[level]][0];
if (delay) {
model.visible = false;
later(instance.showMessage.bind(instance, messagePromise), FOLLOWUP_DELAY);
}
else {
async(instance.showMessage.bind(instance, messagePromise));
}
},
showMessage: function(messagePromise) {
var model = this.model;
model.messagePromise = messagePromise;
model.header = messagePromise.header;
model.content = messagePromise.content;
model.footer = messagePromise.footer;
model.visible = true;
}
});
// instantiate Dialog and make it operational:
dialog = new Dialog();
// return the Class, so it can be subclassed:
return Dialog;
};
},{"./css/dialog.css":179,"event":184,"js-ext":202,"js-ext/extra/classes.js":200,"js-ext/extra/hashmap.js":201,"messages":218,"panel":1927,"polyfill":1930,"utils":1931}],181:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":186,"js-ext/lib/object.js":187,"polyfill/polyfill-base.js":199}],182:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":181}],183:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":181,"js-ext/extra/classes.js":185,"js-ext/lib/object.js":187}],184:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":181,"./event-emitter.js":182,"./event-listener.js":183}],185:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":187,"js-ext/extra/hashmap.js":186,"polyfill/polyfill-base.js":190}],186:[function(require,module,exports){
module.exports=require(4)
},{}],187:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":186,"polyfill/polyfill-base.js":190,"utils":191}],188:[function(require,module,exports){
module.exports=require(14)
},{}],189:[function(require,module,exports){
module.exports=require(15)
},{}],190:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":188,"./lib/window.console.js":189}],191:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":192,"./lib/timers.js":193}],192:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":186,"polyfill/polyfill-base.js":196}],193:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":196}],194:[function(require,module,exports){
module.exports=require(14)
},{}],195:[function(require,module,exports){
module.exports=require(15)
},{}],196:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":194,"./lib/window.console.js":195}],197:[function(require,module,exports){
module.exports=require(14)
},{}],198:[function(require,module,exports){
module.exports=require(15)
},{}],199:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":197,"./lib/window.console.js":198}],200:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":206,"js-ext/extra/hashmap.js":201,"polyfill/polyfill-base.js":211}],201:[function(require,module,exports){
module.exports=require(4)
},{}],202:[function(require,module,exports){
require('./lib/function.js');
require('./lib/object.js');
require('./lib/string.js');
require('./lib/array.js');
require('./lib/json.js');
require('./lib/promise.js');
},{"./lib/array.js":203,"./lib/function.js":204,"./lib/json.js":205,"./lib/object.js":206,"./lib/promise.js":207,"./lib/string.js":208}],203:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":211}],204:[function(require,module,exports){
/**
*
* Pollyfils for often used functionality for Functions
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module js-ext
* @submodule lib/function.js
* @class Function
*
*/
"use strict";
require('polyfill/polyfill-base.js');
var NAME = '[Function]: ';
(function(FunctionPrototype) {
/**
* Sets the context of which the function will be execute. in the
* supplied object's context, optionally adding any additional
* supplied parameters to the end of the arguments the function
* is executed with.
*
* @method rbind
* @param [context] {Object} the execution context.
* The value is ignored if the bound function is constructed using the new operator.
* @param [args*] {any} args* 0..n arguments to append to the end of
* arguments collection supplied to the function.
* @return {function} the wrapped function.
*/
FunctionPrototype.rbind = function (context /*, args* */ ) {
console.log(NAME+'rbind');
var thisFunction = this,
arrayArgs,
slice = Array.prototype.slice;
context || (context = this);
if (arguments.length > 1) {
// removing `context` (first item) by slicing it out:
arrayArgs = slice.call(arguments, 1);
}
return (arrayArgs ?
function () {
// over here, `arguments` will be the "new" arguments when the final function is called!
return thisFunction.apply(context, slice.call(arguments, 0).concat(arrayArgs));
} :
function () {
// over here, `arguments` will be the "new" arguments when the final function is called!
return thisFunction.apply(context, arguments);
}
);
};
}(Function.prototype));
},{"polyfill/polyfill-base.js":211}],205:[function(require,module,exports){
/**
*
* Pollyfils for often used functionality for Arrays
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module js-ext
* @submodule lib/array.js
* @class Array
*
*/
"use strict";
require('polyfill/polyfill-base.js');
var REVIVER = function(key, value) {
return ((typeof value==='string') && value.toDate()) || value;
};
JSON.parseWithDate = function(stringifiedObj) {
return this.parse(stringifiedObj, REVIVER);
};
},{"polyfill/polyfill-base.js":211}],206:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":201,"polyfill/polyfill-base.js":211,"utils":212}],207:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":211}],208:[function(require,module,exports){
module.exports=require(29)
},{}],209:[function(require,module,exports){
module.exports=require(14)
},{}],210:[function(require,module,exports){
module.exports=require(15)
},{}],211:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":209,"./lib/window.console.js":210}],212:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":213,"./lib/timers.js":214}],213:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":201,"polyfill/polyfill-base.js":217}],214:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":217}],215:[function(require,module,exports){
module.exports=require(14)
},{}],216:[function(require,module,exports){
module.exports=require(15)
},{}],217:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":215,"./lib/window.console.js":216}],218:[function(require,module,exports){
(function (global){
/**
* Creating floating Panel-nodes which can be shown and hidden.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
*
* @module panel
* @class Panel
* @since 0.0.1
*/
require('js-ext');
require('polyfill');
(function (global) {
"use strict";
var NAME = '[messages]: ',
MESSAGE_LEVELS = {
1: 'message',
2: 'warning',
3: 'error'
},
createHashMap = require('js-ext/extra/hashmap.js').createMap,
later = require('utils').later,
messages, Event;
global._ITSAmodules || Object.protectedProp(global, '_ITSAmodules', createHashMap());
/*jshint boss:true */
if (messages=global._ITSAmodules.messages) {
/*jshint boss:false */
module.exports = messages; // messages was already created
return;
}
Event = require('event');
messages = {
message: function(message, options) {
var messagePromise = global.Promise.manage(),
emitter, level, timeout;
options || (options={});
emitter = options.emitter || 'global';
level = MESSAGE_LEVELS[options.level] || MESSAGE_LEVELS[1];
timeout = options.timeout;
messagePromise.merge(options);
messagePromise.content = message || '';
if ((typeof timeout==='number') && (timeout>0)) {
// with nothing to notify it was timeout
later(messagePromise.fulfill, timeout);
}
Event.emit(options.target || global, emitter+':'+level, {messagePromise: messagePromise});
return messagePromise;
},
alert: function(message) {
return this.message(message, {
footer: '<button class="pure-button pure-button-primary">Ok</button>'
});
},
warn: function(message) {
return this.message(message, {
footer: '<button class="pure-button pure-button-primary">Ok</button>',
level: 2
});
},
prompt: function(message, defaultValue, label) {
var placeholder = defaultValue ? ' value="'+String(defaultValue)+'"' : '';
if ((typeof message ==='string') || (message!=='')) {
message = '<div class="message">'+message+'</div>';
}
else {
message = '';
}
label = (typeof label==='string') ? (label='<label for="iprompt">'+label+'</label>') : '';
return this.message(
'<div class="pure-form">'+message+label+'<input id="iprompt" type="text"'+placeholder+' fm-defaultitem="true" fm-primaryonenter="true"></div>',
{
footer: '<button is="cancel" class="pure-button">Cancel</button><button is="ok" class="pure-button pure-button-primary">Ok</button>'
}
).then(function(container) {
var button = container.getElement('button');
if (button.getAttr('is')==='ok') {
return container.getElement('input').getValue();
}
});
},
catchErrors: function(catchOrNot) {
// DO NOT use `this` --> when merged, `this` will become the host
// while `global.onerror` refers to `messages`
messages._catchErrors = catchOrNot;
}
};
global.onerror = function(msg, url, line) {
if (messages._catchErrors) {
messages.message(msg, {
header: 'Javascript-error (line '+line+')',
footer: url,
level: 3
});
return true;
}
};
module.exports = messages;
}(typeof global !== 'undefined' ? global : /* istanbul ignore next */ this));
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"event":222,"js-ext":239,"js-ext/extra/hashmap.js":238,"polyfill":257,"utils":258}],219:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":224,"js-ext/lib/object.js":225,"polyfill/polyfill-base.js":237}],220:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":219}],221:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":219,"js-ext/extra/classes.js":223,"js-ext/lib/object.js":225}],222:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":219,"./event-emitter.js":220,"./event-listener.js":221}],223:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":225,"js-ext/extra/hashmap.js":224,"polyfill/polyfill-base.js":228}],224:[function(require,module,exports){
module.exports=require(4)
},{}],225:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":224,"polyfill/polyfill-base.js":228,"utils":229}],226:[function(require,module,exports){
module.exports=require(14)
},{}],227:[function(require,module,exports){
module.exports=require(15)
},{}],228:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":226,"./lib/window.console.js":227}],229:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":230,"./lib/timers.js":231}],230:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":224,"polyfill/polyfill-base.js":234}],231:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":234}],232:[function(require,module,exports){
module.exports=require(14)
},{}],233:[function(require,module,exports){
module.exports=require(15)
},{}],234:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":232,"./lib/window.console.js":233}],235:[function(require,module,exports){
module.exports=require(14)
},{}],236:[function(require,module,exports){
module.exports=require(15)
},{}],237:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":235,"./lib/window.console.js":236}],238:[function(require,module,exports){
module.exports=require(4)
},{}],239:[function(require,module,exports){
module.exports=require(202)
},{"./lib/array.js":240,"./lib/function.js":241,"./lib/json.js":242,"./lib/object.js":243,"./lib/promise.js":244,"./lib/string.js":245}],240:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":248}],241:[function(require,module,exports){
module.exports=require(204)
},{"polyfill/polyfill-base.js":248}],242:[function(require,module,exports){
module.exports=require(205)
},{"polyfill/polyfill-base.js":248}],243:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":238,"polyfill/polyfill-base.js":248,"utils":249}],244:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":248}],245:[function(require,module,exports){
module.exports=require(29)
},{}],246:[function(require,module,exports){
module.exports=require(14)
},{}],247:[function(require,module,exports){
module.exports=require(15)
},{}],248:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":246,"./lib/window.console.js":247}],249:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":250,"./lib/timers.js":251}],250:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":238,"polyfill/polyfill-base.js":254}],251:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":254}],252:[function(require,module,exports){
module.exports=require(14)
},{}],253:[function(require,module,exports){
module.exports=require(15)
},{}],254:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":252,"./lib/window.console.js":253}],255:[function(require,module,exports){
module.exports=require(14)
},{}],256:[function(require,module,exports){
module.exports=require(15)
},{}],257:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":255,"./lib/window.console.js":256}],258:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":259,"./lib/timers.js":260}],259:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":238,"polyfill/polyfill-base.js":263}],260:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":263}],261:[function(require,module,exports){
module.exports=require(14)
},{}],262:[function(require,module,exports){
module.exports=require(15)
},{}],263:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":261,"./lib/window.console.js":262}],264:[function(require,module,exports){
var css = "[plugin-panel=\"true\"] {\n position: absolute !important;\n background-color: #FFF;\n max-width: 90%;\n min-width: 200px;\n min-height: 75px;\n box-shadow: inset 0 0 5px rgba(50, 50, 50, 0.30), 5px 5px 6px rgba(50, 50, 50, 0.45);\n border: solid 1px #000;\n}\n\n[plugin-panel=\"true\"],\n[plugin-panel=\"true\"] >div {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n\n[plugin-panel=\"true\"] >div[is=\"header\"] {\n vertical-align: middle;\n background-color: rgb(0, 100, 192);\n color: #FFF;\n padding: 0 1.5em 0 0.7em;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n vertical-align: middle;\n line-height: 1.75em;\n width: 100%;\n min-height: 1.75em;\n}\n\n[plugin-panel=\"true\"] >div[is=\"content\"] {\n padding: 1.6em 1.2em;\n line-height: 115%;\n}\n\n[plugin-panel=\"true\"] >div[is=\"footer\"] {\n border-top: 1px solid #EAE6DB;\n overflow: hidden;\n vertical-align: middle;\n text-align: right;\n line-height: 1em;\n padding: 0.5em 0.7em;\n width: 100%;\n min-height: 24px;\n}\n\n[plugin-panel=\"true\"] >div[is=\"footer\"] i-button + i-button,\n[plugin-panel=\"true\"] >div[is=\"footer\"] button + button {\n margin-left: 0.5em;\n}\n\n[plugin-panel=\"true\"].itsa-full-draggable {\n cursor: default;\n}\n\n[plugin-panel=\"true\"] >button {\n padding: 0 0.4em 0.1em;\n position: absolute;\n right: 0.2em;\n top: 0.2em;\n z-index: 1;\n}\n\nbody >div[is=\"system-node\"].itsa-modal-layer {\n position: fixed !important;\n top: 0 !important;\n left: 0 !important;\n width: 100% !important;\n height: 100% !important;\n -webkit-box-sizing: border-box !important;\n -moz-box-sizing: border-box !important;\n box-sizing: border-box !important;\n z-index: 1000 !important;\n background-color: #000 !important;\n opacity: 0.2 !important;\n}\n\n[plugin-panel=\"true\"][expand-buttons=\"true\"] >div[is=\"footer\"] i-button + i-button,\n[plugin-panel=\"true\"][expand-buttons=\"true\"] >div[is=\"footer\"] button + button {\n margin-left: 0;\n}\n\n[plugin-panel=\"true\"][expand-buttons=\"true\"] >div[is=\"footer\"] >i-button,\n[plugin-panel=\"true\"][expand-buttons=\"true\"] >div[is=\"footer\"] >button {\n display: block;\n width: 100%;\n margin: 0 0 0.5em;\n}\n\n[plugin-panel=\"true\"][expand-buttons=\"true\"] >div[is=\"footer\"] >i-button:last-child,\n[plugin-panel=\"true\"][expand-buttons=\"true\"] >div[is=\"footer\"] >button:last-child {\n margin-bottom: 0;\n}\n\n@media only screen and (max-width : 480px) {\n [plugin-panel=\"true\"] {\n width: 90%;\n box-shadow: 0 0 6px 6px rgba(50, 50, 50, 0.45);\n }\n}"; (require("/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify"))(css); module.exports = css;
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],265:[function(require,module,exports){
var css = "[dd-draggable] {\n -moz-user-select: none;\n -ms-user-select: none;\n -khtml-user-select: none;\n -webkit-user-select: none;\n user-select: none;\n float: left;\n position: relative;\n}\n.dd-hidden-source {\n visibility: hidden !important;\n}\n.dd-dragging {\n cursor: move;\n}\n.dd-transition {\n -webkit-transition: top 0.25s ease-out, left 0.25s ease-out;\n -moz-transition: top 0.25s ease-out, left 0.25s ease-out;\n -ms-transition: top 0.25s ease-out, left 0.25s ease-out;\n -o-transition: top 0.25s ease-out, left 0.25s ease-out;\n transition: top 0.25s ease-out, left 0.25s ease-out;\n}\n.dd-high-z {\n z-index: 3001 !important;\n}\n.dd-opacity {\n opacity: 0.6;\n filter: alpha(opacity=60); /* For IE8 and earlier */\n}\n[dropzone] {\n position: relative; /* otherwise we cannot place absolute positioned items */\n}"; (require("/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify"))(css); module.exports = css;
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],266:[function(require,module,exports){
"use strict";
/**
* Provides `drag and drop` functionality, without dropzones.
* For `dropzone`-support, you should use the module: `drag-drop`.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @example
* DD = require('drag')(window);
* DD.init();
*
* @module drag
* @class DD
* @since 0.0.4
*/
var NAME = '[drag]: ',
createHashMap = require('js-ext/extra/hashmap.js').createMap,
DRAG = 'drag',
DROP = 'drop',
DRAGGABLE = DRAG+'gable',
DEL_DRAGGABLE = 'del-'+DRAGGABLE,
DD_MINUS = 'dd-',
DD_DRAGGING_CLASS = DD_MINUS+DRAG+'ging',
DD_MASTER_CLASS = DD_MINUS+'master',
DD_HANDLE = DD_MINUS+'handle',
DD_DROPZONE_MOVABLE = DD_MINUS+'dropzone-movable',
CONSTRAIN_ATTR = 'constrain-selector',
MOUSE = 'mouse',
DROPZONE = 'dropzone',
NO_TRANS_CLASS = 'el-notrans', // delivered by `vdom`
HIGH_Z_CLASS = DD_MINUS+'high-z',
REGEXP_NODE_ID = /^#\S+$/,
EMITTER = 'emitter',
DD_EMITTER = DD_MINUS+EMITTER,
DD_DRAG = DD_MINUS+DRAG,
DD_DROP = DD_MINUS+DROP,
DD_FAKE = DD_MINUS+'fake-',
DOWN = 'down',
UP = 'up',
MOVE = 'move',
MOUSEUP = MOUSE+UP,
MOUSEDOWN = MOUSE+DOWN,
MOUSEMOVE = MOUSE+MOVE,
PAN = 'pan',
PANSTART = PAN+'start',
PANMOVE = PAN+MOVE,
PANEND = PAN+'end',
DD_FAKE_MOUSEUP = DD_FAKE+MOUSEUP,
UI = 'UI',
DD_EFFECT_ALLOWED = DD_MINUS+'effect-allowed',
BORDER = 'border',
WIDTH = 'width',
BORDER_LEFT_WIDTH = BORDER+'-left-'+WIDTH,
BORDER_TOP_WIDTH = BORDER+'-top-'+WIDTH,
LEFT = 'left',
TOP = 'top',
WINDOW = 'window',
TRUE = 'true',
NO_OVERFLOW = 'itsa-no-overflow',
DD_MINUSDRAGGABLE = DD_MINUS+DRAGGABLE,
PLUGIN_ATTRS = [DD_MINUS+DROPZONE, CONSTRAIN_ATTR, DD_EMITTER, DD_HANDLE, DD_EFFECT_ALLOWED, DD_DROPZONE_MOVABLE];
require('polyfill');
require('js-ext');
require('./css/drag.css');
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.Drag) {
return window._ITSAmodules.Drag; // Drag was already created
}
var Event = require('event-dom')(window),
isMobile = require('useragent')(window).isMobile,
DOCUMENT = window.document,
bodyNode = DOCUMENT.body,
supportHammer = !!Event.Hammer,
mobileEvents = supportHammer && isMobile,
DD;
require('vdom')(window);
require('node-plugin')(window);
require('window-ext')(window);
DD = {
/**
* Objecthash containing all specific information about the particular drag-cycle.
* It has a structure like this:
*
* ddProps = {
* dragNode {HtmlElement} Element that is dragged
* x {Number} absolute x-position of the draggable inside `document` when the drag starts
* y {Number} absolute y-position of the draggable inside `document` when the drag starts
* inlineLeft {String} inline css of the property `left` when drag starts
* inlineTop {String} inline css of the property `top` when drag starts
* winConstrained {Boolean} whether the draggable should be constrained to `window`
* xMouseLast {Number} absolute x-position of the mouse inside `document` when the drag starts
* yMouseLast {Number} absolute y-position of the draggable inside `document` when the drag starts
* winScrollLeft {Number} the left-scroll of window when drag starts
* winScrollTop {Number} the top-scroll of window when drag starts
* constrain = { // constrain-properties when constrained to a HtmlElement
* xOrig {Number} x-position in the document, included with left-border-width
* yOrig {Number} y-position in the document, included with top-border-width
* x {Number} xOrig corrected with scroll-left of the constrained node
* y {Number} yOrig corrected with scroll-top of the constrained node
* w {Number} scrollWidth
* h {Number} scrollHeight
* };
* relatives[{ // Array with objects that represent all draggables that come along with the master-draggable (in case of multiple items), excluded the master draggable itself
* sourceNode {HtmlElement} original node (defined by drag-drop)
* dragNode {HtmlElement} draggable node
* shiftX {Number} the amount of left-pixels that this HtmlElement differs from the dragged element
* shiftY {Number} the amount of top-pixels that this HtmlElement differs from the dragged element
* inlineLeft {String} inline css of the property `left` when drag starts
* inlineTop {String} inline css of the property `top` when drag starts
* }]
* }
*
* @property ddProps
* @default {}
* @type Object
* @since 0.0.1
*/
ddProps: {},
/**
* Internal hash with notifiers to response after each `Drag` event is set up, or teared down.
* You can use this to hook in into the drag-eventcycle: the `drop`-module uses it this way.
* Is filled by using `notify()`.
*
* @property _notifiers
* @default []
* @type Array
* @private
* @since 0.0.1
*/
_notifiers: [],
/**
* Default function for the `*:dd-drag`-event
*
* @method _defFnDrag
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_defFnDrag: function(e) {
console.log(NAME, '_defFnDrag: default function dd-drag');
var ddProps = this.ddProps,
dragNode = ddProps.dragNode,
constrainNode = ddProps.constrainNode,
winConstrained = ddProps.winConstrained,
x, y;
// is the drag is finished, there will be no ddProps.defined
// return then, to prevent any events that stayed behind
if (!ddProps.defined) {
return;
}
// caution: the user might have put the mouse out of the screen and released the mousebutton!
// If that is the case, the a mouseup-event should be initiated instead of draggin the element
if (e.buttons===0) {
// no more button pressed
/**
* Fired when the mouse comes back into the browser-window while dd-drag was busy yet no buttons are pressed.
* This is a correction to the fact that the mouseup-event wasn't noticed because the mouse was outside the browser.
*
* @event dd-fake-mouseup
* @private
* @since 0.1
*/
Event.emit(dragNode, DD_FAKE_MOUSEUP);
}
else {
console.log(NAME, '_defFnDrag: dragging:');
if (constrainNode) {
ddProps.constrain.x = ddProps.constrain.xOrig - constrainNode.scrollLeft;
ddProps.constrain.y = ddProps.constrain.yOrig - constrainNode.scrollTop;
}
x = ddProps.x+e.xMouse+(winConstrained ? ddProps.winScrollLeft : window.getScrollLeft())-e.xMouseOrigin;
y = ddProps.y+e.yMouse+(winConstrained ? ddProps.winScrollTop : window.getScrollTop())-e.yMouseOrigin;
dragNode.setXY(x, y, ddProps.constrain, true);
ddProps.relatives && ddProps.relatives.forEach(
function(item) {
item.dragNode.setXY(x+item.shiftX, y+item.shiftY, null, true);
}
);
ddProps.winConstrained || dragNode.forceIntoView(true);
constrainNode && dragNode.forceIntoNodeView(constrainNode);
}
},
/**
* Default function for the `*:dd-drop`-event
*
* @method _defFnDrop
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_defFnDrop: function(e) {
console.log(NAME, '_defFnDrop');
var dragNode = e.target,
removeClasses = function (node) {
node.removeClass([NO_TRANS_CLASS, HIGH_Z_CLASS, DD_DRAGGING_CLASS, DEL_DRAGGABLE, DD_MASTER_CLASS]);
};
PLUGIN_ATTRS.forEach(function(attribute) {
var data = '_del_'+attribute;
if (dragNode.getData(data)) {
dragNode.getPlugin('dd').then(function(plugin) {
delete plugin.model[attribute];
});
dragNode.removeData(data);
}
});
removeClasses(dragNode);
e.relatives && e.relatives.forEach(
function(node) {
removeClasses(node);
}
);
},
/**
* Default function for the `*:dd`-event
*
* @method _defFnStart
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_defFnStart: function(e) {
var instance = this,
customEvent;
customEvent = e.emitter + ':'+DD_DRAG;
console.log(NAME, '_defFnStart: default function UI:dd-start. Defining customEvent '+customEvent);
Event.defineEvent(customEvent).defaultFn(instance._defFnDrag.bind(instance));
DOCUMENT.getAll('.'+DD_MASTER_CLASS).removeClass(DD_MASTER_CLASS);
instance._initializeDrag(e);
},
/**
* Defines the definition of the `dd` event: the first phase of the drag-eventcycle (dd, *:dd-drag, *:dd-drop)
*
* @method _defineDDStart
* @param emitterName {String} the emitterName, which leads into the definition of event `emitterName:dd`
* @private
* @since 0.0.1
*/
_defineDDStart: function(emitterName) {
console.log(NAME, '_defineDDStart');
var instance = this;
// by using dd before dd-drag, the user can create a `before`-subscriber to dd
// and define e.emitter and/or e.relatives before going into `dd-drag`
Event.defineEvent(emitterName+':dd')
.defaultFn(instance._defFnStart.bind(instance))
.preventedFn(instance._prevFnStart.bind(instance));
},
/**
* Default function for the `*:dd-drag`-event
*
* @method _initializeDrag
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_initializeDrag: function(e) {
console.log(NAME, '_initializeDrag '+e.xMouseOrigin);
var instance = this,
dragNode = e.target,
constrain = dragNode.getAttr(CONSTRAIN_ATTR),
ddProps = instance.ddProps,
emitterName = e.emitter,
moveEv, x, y, byExactId, match, constrainNode, winConstrained, winScrollLeft, winScrollTop,
xOrig, yOrig;
// define ddProps --> internal object with data about the draggable instance
ddProps.dragNode = dragNode;
ddProps.x = x = dragNode.left;
ddProps.y = y = dragNode.top;
ddProps.inlineLeft = dragNode.getInlineStyle(LEFT);
ddProps.inlineTop = dragNode.getInlineStyle(TOP);
ddProps.winConstrained = winConstrained = (constrain===WINDOW);
ddProps.xMouseLast = x;
ddProps.yMouseLast = y;
if (constrain) {
if (winConstrained) {
ddProps.winScrollLeft = winScrollLeft = window.getScrollLeft();
ddProps.winScrollTop = winScrollTop = window.getScrollTop();
ddProps.constrain = {
x: winScrollLeft,
y: winScrollTop,
w: window.getWidth(),
h: window.getHeight()
};
// if constrained to window:
// set a class that makes overflow hidden --> this will prevent
// some browsers from scrolling the window when a pressed mouse
// gets out of the window
bodyNode.setClass(NO_OVERFLOW);
}
else {
byExactId = REGEXP_NODE_ID.test(constrain);
constrainNode = dragNode.parentNode;
while (constrainNode.matchesSelector && !match) {
match = byExactId ? (constrainNode.id===constrain.substr(1)) : constrainNode.matchesSelector(constrain);
// if there is a match, then make sure x and y fall within the region
if (match) {
ddProps.constrainNode = constrainNode;
xOrig = constrainNode.left + parseInt(constrainNode.getStyle(BORDER_LEFT_WIDTH), 10);
yOrig = constrainNode.top + parseInt(constrainNode.getStyle(BORDER_TOP_WIDTH), 10);
ddProps.constrain = {
xOrig: xOrig,
yOrig: yOrig,
x: xOrig - constrainNode.scrollLeft,
y: yOrig - constrainNode.scrollTop,
w: constrainNode.scrollWidth,
h: constrainNode.scrollHeight
};
}
else {
constrainNode = constrainNode.parentNode;
}
}
}
}
// create listener for `mousemove` and transform it into the `*:dd:drag`-event
moveEv = Event.after(mobileEvents ? PANMOVE : MOUSEMOVE, function(e2) {
if (typeof e2.center==='object') {
e2.clientX = e2.center.x;
e2.clientY = e2.center.y;
}
if (!e2.clientX) {
return;
}
// move the object
e.xMouse = e2.clientX;
e.yMouse = e2.clientY;
/**
* Emitted during the drag-cycle of a draggable Element (while it is dragged).
*
* @event *:dd-drag
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.sourceTarget {HtmlElement} the deepest HtmlElement where the mouse lies upon
* @param e.dd {Promise} Promise that gets fulfilled when dragging is ended. The fullfilled-callback has no arguments.
* @param e.xMouse {Number} the current x-position in the window-view
* @param e.yMouse {Number} the current y-position in the window-view
* @param e.clientX {Number} the current x-position in the window-view
* @param e.clientY {Number} the current y-position in the window-view
* @param e.xMouseOrigin {Number} the original x-position in the document when drag started (incl. scrollOffset)
* @param e.yMouseOrigin {Number} the original y-position in the document when drag started (incl. scrollOffset)
* @param [e.relatives] {NodeList} an optional list that the user could set in a `before`-subscriber of the `dd`-event
* to inform which nodes are related to the draggable node and should be dragged as well.
* @since 0.1
*/
Event.emit(dragNode, emitterName+':'+DD_DRAG, e);
e.dd.callback();
});
// prepare dragNode class for the right CSS:
dragNode.setClass([NO_TRANS_CLASS, HIGH_Z_CLASS, DD_DRAGGING_CLASS]);
Event.onceAfter([mobileEvents ? PANEND : MOUSEUP, DD_FAKE_MOUSEUP], function(e3) {
moveEv.detach();
// set mousepos for the last time:
if (typeof e3.center==='object') {
e3.clientX = e3.center.x;
e3.clientY = e3.center.y;
}
e.xMouse = e3.clientX;
e.yMouse = e3.clientY;
// invoke all teardown notifiers:
instance._notifiers.forEach(
function(notifier) {
notifier.s || notifier.cb.call(notifier.o, e, ddProps);
}
);
if (constrain && ddProps.winConstrained) {
// if constrained to window:
// remove overflow=hidden from the bodynode
bodyNode.removeClass(NO_OVERFLOW);
}
instance.ddProps = {};
/**
* Emitted when drag-cycle of a draggable Element is ended.
*
* @event *:dd-drop
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.sourceTarget {HtmlElement} the deepest HtmlElement where the mouse lies upon
* @param e.dd {Promise} Promise that gets fulfilled when dragging is ended. The fullfilled-callback has no arguments.
* @param e.xMouse {Number} the current x-position in the window-view
* @param e.yMouse {Number} the current y-position in the window-view
* @param e.clientX {Number} the current x-position in the window-view
* @param e.clientY {Number} the current y-position in the window-view
* @param e.xMouseOrigin {Number} the original x-position in the document when drag started (incl. scrollOffset)
* @param e.yMouseOrigin {Number} the original y-position in the document when drag started (incl. scrollOffset)
* @param [e.relatives] {NodeList} an optional list that the user could set in a `before`-subscriber of the `dd`-event
* to inform which nodes are related to the draggable node and should be dragged as well.
* @since 0.1
*/
Event.emit(dragNode, emitterName+':'+DD_DROP, e);
e.dd.fulfill();
});
dragNode.setXY(ddProps.xMouseLast, ddProps.yMouseLast, ddProps.constrain, true);
if (e.relatives) {
// relatives are extra HtmlElements that should be moved aside with the main dragged element
// e.relatives is a selector, e.relativeNodes will be an array with nodes
e.relativeNodes = [];
dragNode.setClass(DD_MASTER_CLASS);
dragNode.setClass(DD_MASTER_CLASS);
ddProps.relatives = [];
e.relatives.forEach(
function(node) {
var item;
if (node !== dragNode) {
item = {
dragNode: node,
shiftX: node.left - x,
shiftY: node.top - y,
inlineLeft: node.getInlineStyle(LEFT),
inlineTop: node.getInlineStyle(TOP)
};
item.dragNode.setClass([NO_TRANS_CLASS, HIGH_Z_CLASS, DD_DRAGGING_CLASS]);
ddProps.relatives.push(item);
e.relativeNodes.push(item.dragNode);
}
}
);
}
// invoke all setup notifiers:
instance._notifiers.forEach(
function(notifier) {
notifier.s && notifier.cb.call(notifier.o, e, ddProps);
}
);
},
/**
* Prevented function for the `*:dd-start`-event
*
* @method _prevFnStart
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_prevFnStart: function(e) {
console.log(NAME, '_prevFnStart');
e.dd.reject();
},
/**
* Engine behind the drag-drop-cycle.
* Sets up a `mousedown` listener to initiate a drag-drop eventcycle. The eventcycle start whenever
* one of these events happens on a HtmlElement with the attribute `dd-draggable="true"`.
* The drag-drop eventcycle consists of the events: `dd-start`, `emitterName:dd-drag` and `emitterName:dd-drop`
*
*
* @method _setupMouseEv
* @private
* @since 0.0.1
*/
_setupMouseEv: function() {
console.log(NAME, '_setupMouseEv: setting up mousedown event');
var instance = this,
nodeTargetFn,
delegatedTargetFn;
nodeTargetFn = function(e) {
var node = e.target,
handle, availableHandles, insideHandle, emitterName;
// first check if there is a handle to determine if the drag started here:
handle = node.getAttr(DD_HANDLE);
if (handle) {
availableHandles = node.getAll(handle);
insideHandle = false;
availableHandles.some(function(handleNode) {
insideHandle = handleNode.contains(e.sourceTarget);
return insideHandle;
});
if (!insideHandle) {
return;
}
}
// initialize ddProps: have to do here, because the event might not start because it wasn't inside the handle when it should be
instance.ddProps = {
defined: true,
dragOverList: []
};
// prevent the emitter from resetting e.target to e.sourceTarget:
e._noResetSourceTarget = true;
// add `dd`-Promise to the eventobject --> this Promise will be resolved once the pointer has released.
e.dd = Promise.manage();
// define e.setOnDrag --> users
e.setOnDrag = function(callbackFn) {
e.dd.setCallback(callbackFn);
};
// store the orriginal mouseposition:
e.xMouseOrigin = e.clientX + window.getScrollLeft();
e.yMouseOrigin = e.clientY + window.getScrollTop();
//set the emitterName:
emitterName = e.target.getAttr(DD_EMITTER) || UI;
// now we can start the eventcycle by emitting emitterName:dd:
/**
* Emitted when a draggable Element's drag-cycle starts. You can use a `before`-subscriber to specify
* e.relatives, which should be a nodelist with HtmlElements, that should be dragged togehter with the master
* draggable Element.
*
* @event *:dd
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.sourceTarget {HtmlElement} the deepest HtmlElement where the mouse lies upon
* @param e.dd {Promise} Promise that gets fulfilled when dragging is ended. The fullfilled-callback has no arguments.
* @param e.xMouse {Number} the current x-position in the window-view
* @param e.yMouse {Number} the current y-position in the window-view
* @param e.clientX {Number} the current x-position in the window-view
* @param e.clientY {Number} the current y-position in the window-view
* @param e.xMouseOrigin {Number} the original x-position in the document when drag started (incl. scrollOffset)
* @param e.yMouseOrigin {Number} the original y-position in the document when drag started (incl. scrollOffset)
* @param [e.relatives] {NodeList} an optional list that the user could set in a `before`-subscriber of the `dd`-event
* to inform which nodes are related to the draggable node and should be dragged as well.
* @since 0.1
*/
instance._defineDDStart(emitterName);
Event.emit(e.target, emitterName+':dd', e);
};
delegatedTargetFn = function(e, cssSelector) {
var container = e.target,
nodelist = container.getAll(cssSelector),
foundNode;
nodelist.some(
function(node) {
(node.contains(e.sourceTarget)) && (foundNode=node);
return foundNode;
}
);
if (foundNode) {
// e.currentTarget = container;
e.target = foundNode;
// Mark the delegated node, so it has the same style as [draggable]:
foundNode.setClass(DEL_DRAGGABLE);
// We must transport the other relevant dd-attributes (and constrain-selector)
// which we will remove when finished dragging:
PLUGIN_ATTRS.forEach(function(attribute) {
var attr = container.getAttr(attribute);
if (attr && !foundNode.hasAttr(attribute)) {
foundNode.setData('_del_'+attribute, attr);
foundNode.setAttr(attribute, attr);
}
});
nodeTargetFn(e);
}
};
Event.after(mobileEvents ? PANSTART : MOUSEDOWN, function(e) {
var draggableAttr = e.target.getAttr(DD_MINUSDRAGGABLE);
if (typeof e.center==='object') {
e.clientX = e.center.x;
e.clientY = e.center.y;
}
(draggableAttr===TRUE) ? nodeTargetFn(e) : delegatedTargetFn(e, draggableAttr);
}, '['+DD_MINUSDRAGGABLE+']');
},
/**
* Initializes dragdrop. Needs to be invoked, otherwise DD won't run.
*
* @method init
* @param dragableElement {HtmlElement} HtmlElement that is checked for its allowed effects
* @return {Boolean} if copy-dragables are allowed
* @since 0.0.1
*/
init: function() {
console.log(NAME, 'init');
var instance = this;
if (!instance._inited) {
instance._setupMouseEv(); // engine behind the dragdrop-eventcycle
if (mobileEvents) {
Event.before(['touchstart', 'touchmove'], function(ev) {
(instance.ddProps.size()>0) && ev.preventDefault();
});
}
Event.defineEvent('UI:'+DD_DROP)
.defaultFn(instance._defFnDrop.rbind(instance));
}
instance._inited = true;
},
/**
* Creates a notifier to response after each `Drag` event is set up, or teared down.
* You can use this to hook in into the drag-eventcycle: the `drop`-module uses it this way.
*
* @static
* @method notify
* @param callback {Function} subscriber: will be invoked after every drag-event is set up.
* Recieves 2 arguments: the `eventobject` and the internal property: `ddProps`
* @param context {Object} context of the callback
* @param setup {Boolean} wheter the callback should be invoked on setup (true) or teardown (false)
* @return {Object} handle with a method `detach()` which you can use to remove it from the `notifier-hash`
* @since 0.0.1
*/
notify: function(callback, context, setup) {
console.log(NAME, 'notify');
var notifier = {
cb: callback,
o: context,
s: setup
};
this._notifiers.push(notifier);
return {
detach: function() {
this._notifiers.remove(notifier);
}
};
}
};
DOCUMENT.definePlugin('dd', null, {
attrs: {
draggable: 'string',
handle: 'string',
emitter: 'string'
},
defaults: {
draggable: 'true'
}
});
window._ITSAmodules.Drag = DD;
return DD;
};
},{"./css/drag.css":265,"event-dom":267,"js-ext":363,"js-ext/extra/hashmap.js":362,"node-plugin":379,"polyfill":551,"useragent":567,"vdom":620,"window-ext":621}],267:[function(require,module,exports){
"use strict";
/**
* Integrates DOM-events to event. more about DOM-events:
* http://www.smashingmagazine.com/2013/11/12/an-introduction-to-dom-events/
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @example
* Event = require('event-dom')(window);
*
* @module event
* @submodule event-dom
* @class Event
* @since 0.0.1
*/
var NAME = '[event-dom]: ',
Event = require('event'),
later = require('utils').later,
createHashMap = require('js-ext/extra/hashmap.js').createMap,
OUTSIDE = 'outside',
REGEXP_NODE_ID = /^#\S+$/,
REGEXP_EXTRACT_NODE_ID = /#(\S+)/,
REGEXP_UI_OUTSIDE = /^.+outside$/,
TIME_BTN_PRESSED = 200,
PURE_BUTTON_ACTIVE = 'pure-button-active',
UI = 'UI:',
NODE = 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
TAP = 'tap',
CLICK = 'click',
RIGHTCLICK = 'right'+CLICK,
CENTERCLICK = 'center'+CLICK,
EV_REMOVED = UI+NODE+REMOVE,
EV_INSERTED = UI+NODE+INSERT,
EV_CONTENT_CHANGE = UI+NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = UI+ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = UI+ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = UI+ATTRIBUTE+INSERT,
mutationEventsDefined = false,
NO_DEEP_SEARCH = {},
/*
* Internal hash containing all DOM-events that are listened for (at `document`).
*
* DOMEvents = {
* 'tap': callbackFn,
* 'mousemove': callbackFn,
* 'keypress': callbackFn
* }
*
* @property DOMEvents
* @default {}
* @type Object
* @private
* @since 0.0.1
*/
DOMEvents = {};
require('js-ext/lib/string.js');
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('polyfill/polyfill-base.js');
module.exports = function (window) {
var DOCUMENT = window.document,
_domSelToFunc, _evCallback, _findCurrentTargets, _preProcessor, _setupEvents, _setupMutationListener, _teardownMutationListener,
_setupDomListener, _teardownDomListener, SORT, _sortFunc, _sortFuncReversed, _getSubscribers, _selToFunc, MUTATION_EVENTS, preventClick;
require('vdom')(window);
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.EventDom) {
return Event; // Event was already extended
}
MUTATION_EVENTS = [EV_REMOVED, EV_INSERTED, EV_CONTENT_CHANGE, EV_ATTRIBUTE_REMOVED, , EV_ATTRIBUTE_CHANGED, EV_ATTRIBUTE_INSERTED];
/*
* Transforms the selector to a valid function
*
* @method _selToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_selToFunc = function(customEvent, subscriber) {
Event._sellist.some(function(selFn) {
return selFn(customEvent, subscriber);
});
};
/*
* Creates a filterfunction out of a css-selector. To be used for catching any dom-element, without restrictions
* of any context (like Parcels can --> Parcel.Event uses _parcelSelToDom instead)
* On "non-outside" events, subscriber.t is set to the node that first matches the selector
* so it can be used to set as e.target in the final subscriber
*
* @method _domSelToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_domSelToFunc = function(customEvent, subscriber) {
// this stage is runned during subscription
var outsideEvent = REGEXP_UI_OUTSIDE.test(customEvent),
selector = subscriber.f,
context = subscriber.o,
vnode = subscriber.o.vnode,
isCustomElement = vnode && vnode.isItag,
visibleContent = isCustomElement && !vnode.domNode.contentHidden,
nodeid, byExactId, newTarget, deepSearch;
isCustomElement = false;
console.log(NAME, '_domSelToFunc type of selector = '+typeof selector);
// note: selector could still be a function: in case another subscriber
// already changed it.
if ((!selector && !isCustomElement) || (typeof selector === 'function')) {
subscriber.n || (subscriber.n = isCustomElement ? context : DOCUMENT);
return true;
}
selector || (selector='');
nodeid = selector.match(REGEXP_EXTRACT_NODE_ID);
nodeid ? (subscriber.nId=nodeid[1]) : (subscriber.n=isCustomElement ? context : DOCUMENT);
byExactId = REGEXP_NODE_ID.test(selector);
deepSearch = !NO_DEEP_SEARCH[customEvent];
// set the selector to `subscriber._s` so that e.currentTarget can calculate it:
subscriber._s = selector;
subscriber.f = function(e) {
// this stage is runned when the event happens
console.log(NAME, '_domSelToFunc inside filter. selector: '+selector);
var node = e.target,
vnode = node.vnode,
character1 = selector && selector.substr(1),
match = false;
if (!isCustomElement || visibleContent || context.contains(node)) {
if (selector==='') {
match = true;
}
else {
// e.target is the most deeply node in the dom-tree that caught the event
// our listener uses `selector` which might be a node higher up the tree.
// we will reset e.target to this node (if there is a match)
// note that e.currentTarget will always be `document` --> we're not interested in that
// also, we don't check for `node`, but for node.matchesSelector: the highest level `document`
// is not null, yet it doesn't have .matchesSelector so it would fail
// we DON'T want this for the focus and blur event!
if (deepSearch) {
if (vnode) {
// we go through the vdom
if (!vnode.removedFromDOM) {
while (vnode && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = vnode.domNode;
}
vnode = vnode.vParent;
}
}
}
else {
// we go through the dom
while (node.matchesSelector && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the dom');
match = byExactId ? (node.id===character1) : node.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = node;
}
node = node.parentNode;
}
}
}
else {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
if (vnode) {
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
}
else {
match = node.matchesSelector && (byExactId ? (node.id===character1) : node.matchesSelector(selector));
}
match && (e.sourceTarget=node);
}
}
}
if (outsideEvent && !match) {
// there is a match for the outside-event:
// we need to set e.sourceTarget and e.target:
newTarget = DOCUMENT.getElement(selector, true);
if (newTarget) {
e.sourceTarget = node;
subscriber.t = newTarget;
}
else {
// make return `false` because the selector is not in the dom
match = true;
}
}
console.log(NAME, '_domSelToFunc filter returns '+(!outsideEvent ? match : !match));
return !outsideEvent ? match : !match;
};
return true;
};
// at this point, we need to find out what are the current node-refs. whenever there is
// a filter that starts with `#` --> in those cases we have a bubble-chain, because the selector isn't
// set up with `document` at its root.
// we couldn't do this at time of subscribtion, for the nodes might not be there at that time.
// however, we only need to do this once: we store the value if we find them
// no problem when the nodes leave the dom later: the previous filter wouldn't pass
_findCurrentTargets = function(subscribers) {
console.log(NAME, '_findCurrentTargets');
subscribers.forEach(
function(subscriber) {
console.log(NAME, '_findCurrentTargets for single subscriber. nId: '+subscriber.nId);
subscriber.nId && (subscriber.n=DOCUMENT.getElementById(subscriber.nId, true));
}
);
};
/*
* Generates an event through our Event-system. Does the actual transportation from DOM-events
* into our Eventsystem. It also looks at the response of our Eventsystem: if our system
* halts or preventDefaults the customEvent, then the original DOM-event will be preventDefaulted.
*
* @method _evCallback
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_evCallback = function(e) {
console.log(NAME, '_evCallback');
var allSubscribers = Event._subs,
eType = e.type,
eventobject, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs, subsOutside,
subscribers, eventobjectOutside, wildcard_named_subsOutside, customEvent, eventName, which;
eventName = eType;
// first: a `click` event might be needed to transformed into `rightclick`:
if (eventName===CLICK) {
which = e.which;
(which===2) && (eventName=CENTERCLICK);
(which===3) && (eventName=RIGHTCLICK);
}
if (eventName==='tap') {
// prevent the next click-event
preventClick = true;
e.clientX || (e.clientX = e.center && e.center.x);
e.clientY || (e.clientY = e.center && e.center.y);
}
else if (preventClick && (eventName===CLICK)) {
preventClick = false;
return;
}
if (eventName===CLICK) {
eventName = 'tap';
e.center = {
x: e.clientX,
y: e.clientY
};
e.eventType = 4;
e.pointerType = 'mouse';
e.tapCount = 1;
}
customEvent = 'UI:'+eventName;
subs = allSubscribers[customEvent];
wildcard_named_subs = allSubscribers['*:'+eventName];
named_wildcard_subs = allSubscribers['UI:*'];
wildcard_wildcard_subs = allSubscribers['*:*'];
// Emit the dom-event though our eventsystem:
// NOTE: emit() needs to be synchronous! otherwise we wouldn't be able
// to preventDefault in time
//
// e = eventobject from the DOM-event OR gesture-event
// eventobject = eventobject from our Eventsystem, which get returned by calling `emit()`
// now so the work:
subscribers = _getSubscribers(e, true, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
eventobject = Event._emit(e.target, customEvent, e, subscribers, [], _preProcessor, false, true);
// now check outside subscribers
subsOutside = allSubscribers[customEvent+OUTSIDE];
wildcard_named_subsOutside = allSubscribers['*:'+eventName+OUTSIDE];
subscribers = _getSubscribers(e, true, subsOutside, wildcard_named_subsOutside);
eventobjectOutside = Event._emit(e.target, customEvent+OUTSIDE, e, subscribers, [], _preProcessor, false, true);
// if eventobject was preventdefaulted or halted: take appropriate action on
// the original dom-event. Note: only the original event can caused this, not the outsideevent
// stopPropagation on the original eventobject has no impact on our eventsystem, but who know who else is watching...
// be carefull though: not all gesture events have e.stopPropagation
eventobject.status.halted && e.stopPropagation && e.stopPropagation();
// now we might need to preventDefault the original event.
// be carefull though: not all gesture events have e.preventDefault
if ((eventobject.status.halted || eventobject.status.defaultPrevented || eventobject.status.defaultPreventedContinue) && e.preventDefault) {
e.preventDefault();
}
if (eventobject.status.ok) {
// last step: invoke the aftersubscribers
// we need to do this asynchronous: this way we pass them AFTER the DOM-event's defaultFn
// also make sure to paas-in the payload of the manipulated eventobject
subscribers = _getSubscribers(e, false, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent, eventobject, [], subscribers, _preProcessor, true), 10);
// now check outside subscribers
subscribers = _getSubscribers(e, false, subsOutside, wildcard_named_subsOutside);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent+OUTSIDE, eventobjectOutside, [], subscribers, _preProcessor, true), 10);
}
};
/*
* Creates an array of subscribers in the right order, conform their position in the DOM.
* Only subscribers that match the filter are involved.
*
* @method _getSubscribers
* @param e {Object} eventobject
* @param before {Boolean} whether it is a before- or after-subscriber
* @param subs {Array} array with subscribers
* @param wildcard_named_subs {Array} array with subscribers
* @param named_wildcard_subs {Array} array with subscribers
* @param wildcard_wildcard_subs {Array} array with subscribers
* @private
* @since 0.0.1
*/
_getSubscribers = function(e, before, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs) {
var subscribers = [],
beforeOrAfter = before ? 'b' : 'a',
saveConcat = function(extrasubs) {
extrasubs && extrasubs[beforeOrAfter] && (subscribers=subscribers.concat(extrasubs[beforeOrAfter]));
};
saveConcat(subs);
saveConcat(wildcard_named_subs);
saveConcat(named_wildcard_subs);
saveConcat(wildcard_wildcard_subs);
if (subscribers.length>0) {
subscribers = function(array, testFunc) {
// quickest way to filter an array: see http://jsperf.com/array-filter-performance/4
var filtered = array.slice(0), i;
for (i=array.length-1; i>=0; i--) {
console.log(NAME, 'filtercheck for subscriber');
testFunc(array[i]) || filtered.splice(i, 1);
}
return filtered;
}(subscribers, function(subscriber) {return (!subscriber.f || subscriber.f.call(subscriber.o, e));});
if (subscribers.length>0) {
// _findCurrentTargets(subscribers);
// sorting, based upon the sortFn
subscribers.sort(SORT);
}
}
return subscribers;
};
/*
* Sets e.target and e.sourceTarget for the single subscriber.
* Needs to be done for evenry single subscriber, because with a single event, these values change for each subscriber
*
* @method _preProcessor
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_preProcessor = function(subscriber, e) {
console.log(NAME, '_preProcessor');
// inside the aftersubscribers, we may need exit right away.
// this would be the case whenever stopPropagation or stopImmediatePropagation was called
// in case the subscribernode equals the node on which stopImmediatePropagation was called: return true
var propagationStopped, immediatePropagationStopped,
targetnode = (subscriber.t || subscriber.n);
immediatePropagationStopped = e.status.immediatePropagationStopped;
if (immediatePropagationStopped && ((immediatePropagationStopped===targetnode) || !immediatePropagationStopped.contains(targetnode))) {
console.log(NAME, '_preProcessor will return true because of immediatePropagationStopped');
return true;
}
// in case the subscribernode does not fall within or equals the node on which stopPropagation was called: return true
propagationStopped = e.status.propagationStopped;
if (propagationStopped && (propagationStopped!==targetnode) && !propagationStopped.contains(targetnode)) {
console.log(NAME, '_preProcessor will return true because of propagationStopped');
return true;
}
e._s = subscriber._s;
// now we might need to set e.target to the right node:
// the filterfunction might have found the true domnode that should act as e.target
// and set it at subscriber.t
// also, we need to backup the original e.target: this one should be reset when
// we encounter a subscriber with its own filterfunction instead of selector
if (subscriber.t) {
e.sourceTarget || (e.sourceTarget=e.target);
e.target = subscriber.t;
}
else {
e.sourceTarget && (e.target=e.sourceTarget);
}
return false;
};
/*
* Transports DOM-events to the Event-system. Catches events at their most early stage:
* their capture-phase. When these events happen, a new customEvent is generated by our own
* Eventsystem, by calling _evCallback(). This way we keep DOM-events and our Eventsystem completely separated.
*
* @method _setupDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_setupDomListener = function(customEvent, subscriber) {
console.log(NAME, '_setupDomListener');
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0],
eventName = eventSplitted[1],
outsideEvent = REGEXP_UI_OUTSIDE.test(eventName);
// be careful: anyone could also register an `outside`-event.
// in those cases, the DOM-listener must be set up without `outside`
outsideEvent && (eventName=eventName.substring(0, eventName.length-7));
// if eventName equals `mouseover` or `mouseleave` then we quit:
// people should use `mouseover` and `mouseout`
if ((eventName==='mouseenter') || (eventName==='mouseleave')) {
console.warn(NAME, 'Subscription to '+eventName+' not supported, use mouseover and mouseout: this eventsystem uses these non-noisy so they act as mouseenter and mouseleave');
return;
}
// only accept tap-events, yet later on we WILL need to listen for click-events
(eventName===CLICK) && (eventName=TAP);
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(emitterName+':'+eventName+(outsideEvent ? OUTSIDE : ''), subscriber);
// already registered? then return, also return if someone registered for UI:*
if (DOMEvents[eventName] || (eventName==='*')) {
// cautious: one might have registered the event, but not yet the outsideevent.
// in that case: save this setting:
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
return;
}
DOMEvents[eventName] = true;
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
// one exception: windowresize should listen to the window-object
if (eventName==='resize') {
window.addEventListener(eventName, _evCallback);
}
else {
((eventName===RIGHTCLICK) || (eventName===CENTERCLICK)) && (eventName=TAP);
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.addEventListener(eventName, _evCallback, true);
// listen for both `tap` and `click` events to happen
(eventName===TAP) && DOCUMENT.addEventListener(CLICK, _evCallback, true);
}
};
_setupEvents = function() {
var lastFocussed;
// make sure disabled buttons don't work:
Event.before(['tap', 'press'], function(e) {
e.preventDefault();
}, '.pure-button-disabled, button[disabled]');
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.before(
'keydown',
function(e) {
e._buttonPressed = true;
Event.emit(e.target, 'UI:tap', e);
},
function(e) {
var keyCode = e.keyCode;
return (e.target.getTagName()==='BUTTON') && ((keyCode===13) || (keyCode===32));
}
);
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.after(
'tap',
function(e) {
var buttonNode = e.target;
if (e._buttonPressed) {
buttonNode.setClass(PURE_BUTTON_ACTIVE);
e._noRender = true;
// even if the node isn't in the DOM, we can still try to manipulate it:
// the vdom makes sure no errors occur when the node is already removed
later(buttonNode.removeClass.bind(buttonNode, PURE_BUTTON_ACTIVE), TIME_BTN_PRESSED);
}
}
);
// fix activeElement on Mac
Event.before('focus', function(e) {
// will come here more often because of bubblechain
// however, the last pass-through will set the deepest node
lastFocussed = e.target;
});
// patching DOCUMENT.activeElement because it doesn't work well in a Mac: https://developer.mozilla.org/en-US/docs/Web/API/document.activeElement
// DOCUMENT._activeElement is used with the patch for DOCUMENT.activeElement its getter
Event.after('focus', function() {
DOCUMENT._activeElement = lastFocussed;
});
Event.before('blur', function() {
DOCUMENT._activeElement = null;
});
// Note: window.document has no prototype
Object.defineProperty(DOCUMENT, 'activeElement', {
get: function() {
return DOCUMENT._activeElement || DOCUMENT.body;
}
});
};
_setupMutationListener = function() {
DOCUMENT.hasMutationSubs = true;
if (!mutationEventsDefined) {
Event.defineEvent(EV_REMOVED).unPreventable();
Event.defineEvent(EV_INSERTED).unPreventable();
Event.defineEvent(EV_CONTENT_CHANGE).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_REMOVED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_CHANGED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_INSERTED).unPreventable();
mutationEventsDefined = true;
}
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFunc = function(subscriberOne, subscriberTwo) {
return (subscriberTwo.t || subscriberTwo.n).contains(subscriberOne.t || subscriberOne.n) ? -1 : 1;
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFuncReversed = function(subscriberOne, subscriberTwo) {
return (subscriberOne.t || subscriberOne.n).contains(subscriberTwo.t || subscriberTwo.n) ? 1 : -1;
};
/*
* Removes DOM-eventsubscribers from document when they are no longer needed.
*
* @method _teardownDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @private
* @since 0.0.2
*/
_teardownDomListener = function(customEvent) {
var customEventWithoutOutside = customEvent.endsWith(OUTSIDE) ? customEvent.substr(0, customEvent.length-7) : customEvent,
eventSplitted = customEventWithoutOutside.split(':'),
eventName = eventSplitted[1],
stillInUse;
if ((customEventWithoutOutside===CLICK) || (customEventWithoutOutside===RIGHTCLICK) || (customEventWithoutOutside===CENTERCLICK)) {
stillInUse = Event._subs[CLICK] ||
Event._subs[CLICK+OUTSIDE];
Event._subs[RIGHTCLICK] ||
Event._subs[RIGHTCLICK+OUTSIDE],
Event._subs[CENTERCLICK] ||
Event._subs[CENTERCLICK+OUTSIDE];
eventName = CLICK;
}
else {
stillInUse = Event._subs[customEventWithoutOutside] || Event._subs[customEventWithoutOutside+OUTSIDE];
}
if (!stillInUse) {
console.log(NAME, '_teardownDomListener '+customEvent);
// remove eventlistener from `document`
// one exeption: windowresize should listen to the window-object
if (eventName==='resize') {
window.removeEventListener(eventName, _evCallback);
}
else {
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.removeEventListener(eventName, _evCallback, true);
}
delete DOMEvents[eventName];
}
};
_teardownMutationListener = function() {
if (!Event._subs[EV_REMOVED] &&
!Event._subs[EV_INSERTED] &&
!Event._subs[EV_CONTENT_CHANGE] &&
!Event._subs[EV_ATTRIBUTE_REMOVED] &&
!Event._subs[EV_ATTRIBUTE_CHANGED] &&
!Event._subs[EV_ATTRIBUTE_INSERTED]
) {
DOCUMENT.hasMutationSubs = false;
}
};
// Now a very tricky one:
// Some browsers do an array.sort down-top instead of top-down.
// In those cases we need another sortFn, for the position on an equal match should fall
// behind instead of before (which is the case on top-down sort)
[1,2].sort(function(a /*, b */) {
SORT || (SORT=(a===2) ? _sortFuncReversed : _sortFunc);
});
// Now we do some initialization in order to make DOM-events work:
Object.defineProperty(Event._defaultEventObj, 'currentTarget', {
get: function() {
var ev = this,
e_target = ev.target;
if (e_target && ev._s) {
if (e_target.matches(ev._s)) {
return e_target;
}
return e_target.inside(ev._s) || undefined;
}
}
});
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(UI+'*', _setupDomListener, Event)
._setEventObjProperty('stopPropagation', function() {this.status.ok || (this.status.propagationStopped = this.target);})
._setEventObjProperty('stopImmediatePropagation', function() {this.status.ok || (this.status.immediatePropagationStopped = this.target);});
// Notify when someone subscribes to any event at all --> we might need to transform the filterFn from a selector into a true fnction
// this is already done automaticly by _setupDomListener fo UI:* events
Event.notify('*:*', function(customEvent, subscriber) {
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0];
if ((emitterName!=='UI') && (typeof subscriber.f==='string')) {
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(customEvent, subscriber);
}
}, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(UI+'*', _teardownDomListener, Event);
Event._sellist = [_domSelToFunc];
_setupEvents();
// making HTMLElement to be able to emit using event-emitter:
(function(HTMLElementPrototype) {
HTMLElementPrototype.merge(Event.Emitter('UI'));
}(window.HTMLElement.prototype));
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(MUTATION_EVENTS, _setupMutationListener, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(MUTATION_EVENTS, _teardownMutationListener, Event);
// Note: window.document has no prototype
DOCUMENT.suppressMutationEvents = function(suppress) {
this._suppressMutationEvents = suppress;
};
// Event.noDeepDomEvt and Event._domCallback are the only method that is added to Event.
// We need to do this, because `event-mobile` needs access to the same method.
// We could have done without this method and instead listen for a custom-event to handle
// Mobile events, however, that would lead into 2 eventcycli which isn't performant.
/**
*
* @method noDeepDomEvt
* @param domEvent {String} the eventName that should be processed without deepsearch
* @param e {Object} eventobject
* @for Event
* @chainable
* @since 0.0.1
*/
Event.noDeepDomEvt = function(domEvent) {
domEvent.contains(':') || (domEvent=UI+domEvent);
NO_DEEP_SEARCH[domEvent] = true;
return this;
};
/**
* Does the actual transportation from DOM-events into the Eventsystem. It also looks at the response of
* the Eventsystem: on e.halt() or e.preventDefault(), the original DOM-event will be preventDefaulted.
*
* @method _domCallback
* @param eventName {String} the customEvent that is transported to the eventsystem
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
Event._domCallback = function(e) {
_evCallback(e);
};
// store module:
window._ITSAmodules.EventDom = Event;
return Event;
};
},{"event":271,"js-ext/extra/hashmap.js":287,"js-ext/lib/array.js":288,"js-ext/lib/object.js":289,"js-ext/lib/string.js":290,"polyfill/polyfill-base.js":302,"utils":303,"vdom":361}],268:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":273,"js-ext/lib/object.js":274,"polyfill/polyfill-base.js":286}],269:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":268}],270:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":268,"js-ext/extra/classes.js":272,"js-ext/lib/object.js":274}],271:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":268,"./event-emitter.js":269,"./event-listener.js":270}],272:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":274,"js-ext/extra/hashmap.js":273,"polyfill/polyfill-base.js":277}],273:[function(require,module,exports){
module.exports=require(4)
},{}],274:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":273,"polyfill/polyfill-base.js":277,"utils":278}],275:[function(require,module,exports){
module.exports=require(14)
},{}],276:[function(require,module,exports){
module.exports=require(15)
},{}],277:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":275,"./lib/window.console.js":276}],278:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":279,"./lib/timers.js":280}],279:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":273,"polyfill/polyfill-base.js":283}],280:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":283}],281:[function(require,module,exports){
module.exports=require(14)
},{}],282:[function(require,module,exports){
module.exports=require(15)
},{}],283:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":281,"./lib/window.console.js":282}],284:[function(require,module,exports){
module.exports=require(14)
},{}],285:[function(require,module,exports){
module.exports=require(15)
},{}],286:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":284,"./lib/window.console.js":285}],287:[function(require,module,exports){
module.exports=require(4)
},{}],288:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":293}],289:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":287,"polyfill/polyfill-base.js":293,"utils":294}],290:[function(require,module,exports){
module.exports=require(29)
},{}],291:[function(require,module,exports){
module.exports=require(14)
},{}],292:[function(require,module,exports){
module.exports=require(15)
},{}],293:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":291,"./lib/window.console.js":292}],294:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":295,"./lib/timers.js":296}],295:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":287,"polyfill/polyfill-base.js":299}],296:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":299}],297:[function(require,module,exports){
module.exports=require(14)
},{}],298:[function(require,module,exports){
module.exports=require(15)
},{}],299:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":297,"./lib/window.console.js":298}],300:[function(require,module,exports){
module.exports=require(14)
},{}],301:[function(require,module,exports){
module.exports=require(15)
},{}],302:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":300,"./lib/window.console.js":301}],303:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":304,"./lib/timers.js":305}],304:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":287,"polyfill/polyfill-base.js":308}],305:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":308}],306:[function(require,module,exports){
module.exports=require(14)
},{}],307:[function(require,module,exports){
module.exports=require(15)
},{}],308:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":306,"./lib/window.console.js":307}],309:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],310:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":314,"js-ext/extra/hashmap.js":311,"polyfill/polyfill-base.js":320}],311:[function(require,module,exports){
module.exports=require(4)
},{}],312:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":313,"../lib/object.js":314,"./classes.js":310,"js-ext/extra/hashmap.js":311,"polyfill/lib/weakmap.js":318}],313:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":320}],314:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":311,"polyfill/polyfill-base.js":320,"utils":321}],315:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":320}],316:[function(require,module,exports){
module.exports=require(29)
},{}],317:[function(require,module,exports){
module.exports=require(14)
},{}],318:[function(require,module,exports){
module.exports=require(57)
},{}],319:[function(require,module,exports){
module.exports=require(15)
},{}],320:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":317,"./lib/window.console.js":319}],321:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":322,"./lib/timers.js":323}],322:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":311,"polyfill/polyfill-base.js":326}],323:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":326}],324:[function(require,module,exports){
module.exports=require(14)
},{}],325:[function(require,module,exports){
module.exports=require(15)
},{}],326:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":324,"./lib/window.console.js":325}],327:[function(require,module,exports){
module.exports=require(66)
},{}],328:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":327}],329:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":327}],330:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":327}],331:[function(require,module,exports){
module.exports=require(14)
},{}],332:[function(require,module,exports){
module.exports=require(15)
},{}],333:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":331,"./lib/window.console.js":332}],334:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":335,"./lib/timers.js":336}],335:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":311,"polyfill/polyfill-base.js":339}],336:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":339}],337:[function(require,module,exports){
module.exports=require(14)
},{}],338:[function(require,module,exports){
module.exports=require(15)
},{}],339:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":337,"./lib/window.console.js":338}],340:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":341}],341:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":342,"js-ext/lib/object.js":343}],342:[function(require,module,exports){
module.exports=require(4)
},{}],343:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":342,"polyfill/polyfill-base.js":346,"utils":347}],344:[function(require,module,exports){
module.exports=require(14)
},{}],345:[function(require,module,exports){
module.exports=require(15)
},{}],346:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":344,"./lib/window.console.js":345}],347:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":348,"./lib/timers.js":349}],348:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":342,"polyfill/polyfill-base.js":352}],349:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":352}],350:[function(require,module,exports){
module.exports=require(14)
},{}],351:[function(require,module,exports){
module.exports=require(15)
},{}],352:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":350,"./lib/window.console.js":351}],353:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":311,"js-ext/lib/object.js":314,"js-ext/lib/string.js":316,"polyfill":333,"polyfill/extra/transition.js":328,"polyfill/extra/vendorCSS.js":330}],354:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":311,"js-ext/lib/object.js":314,"polyfill":333}],355:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":311,"js-ext/lib/object.js":314,"js-ext/lib/string.js":316,"polyfill":333}],356:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":309,"./attribute-extractor.js":353,"./element-array.js":354,"./html-parser.js":357,"./node-parser.js":358,"./vdom-ns.js":359,"./vnode.js":360,"js-ext/extra/hashmap.js":311,"js-ext/lib/object.js":314,"js-ext/lib/promise.js":315,"js-ext/lib/string.js":316,"polyfill":333,"polyfill/extra/transition.js":328,"polyfill/extra/transitionend.js":329,"polyfill/extra/vendorCSS.js":330,"utils":334,"window-ext":340}],357:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":353,"./vdom-ns.js":359,"js-ext/extra/hashmap.js":311,"js-ext/lib/object.js":314,"polyfill":333}],358:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":353,"./vdom-ns.js":359,"./vnode.js":360,"js-ext/extra/hashmap.js":311,"js-ext/lib/object.js":314,"polyfill":333}],359:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":311,"js-ext/lib/object.js":314,"polyfill":333}],360:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":353,"./html-parser.js":357,"./vdom-ns.js":359,"js-ext/extra/hashmap.js":311,"js-ext/extra/lightmap.js":312,"js-ext/lib/array.js":313,"js-ext/lib/object.js":314,"js-ext/lib/string.js":316,"polyfill":333,"utils/lib/timers.js":336}],361:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":355,"./partials/extend-element.js":356,"./partials/node-parser.js":358,"js-ext/extra/hashmap.js":311,"js-ext/lib/object.js":314,"utils/lib/timers.js":336}],362:[function(require,module,exports){
module.exports=require(4)
},{}],363:[function(require,module,exports){
module.exports=require(202)
},{"./lib/array.js":364,"./lib/function.js":365,"./lib/json.js":366,"./lib/object.js":367,"./lib/promise.js":368,"./lib/string.js":369}],364:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":372}],365:[function(require,module,exports){
module.exports=require(204)
},{"polyfill/polyfill-base.js":372}],366:[function(require,module,exports){
module.exports=require(205)
},{"polyfill/polyfill-base.js":372}],367:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":362,"polyfill/polyfill-base.js":372,"utils":373}],368:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":372}],369:[function(require,module,exports){
module.exports=require(29)
},{}],370:[function(require,module,exports){
module.exports=require(14)
},{}],371:[function(require,module,exports){
module.exports=require(15)
},{}],372:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":370,"./lib/window.console.js":371}],373:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":374,"./lib/timers.js":375}],374:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":362,"polyfill/polyfill-base.js":378}],375:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":378}],376:[function(require,module,exports){
module.exports=require(14)
},{}],377:[function(require,module,exports){
module.exports=require(15)
},{}],378:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":376,"./lib/window.console.js":377}],379:[function(require,module,exports){
arguments[4][5][0].apply(exports,arguments)
},{"event-dom":380,"js-ext/extra/classes.js":475,"js-ext/extra/hashmap.js":476,"js-ext/lib/object.js":477,"js-ext/lib/promise.js":478,"js-ext/lib/string.js":479,"polyfill":491,"utils/lib/timers.js":492,"vdom":548}],380:[function(require,module,exports){
module.exports=require(267)
},{"event":384,"js-ext/extra/hashmap.js":400,"js-ext/lib/array.js":401,"js-ext/lib/object.js":402,"js-ext/lib/string.js":403,"polyfill/polyfill-base.js":415,"utils":416,"vdom":474}],381:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":386,"js-ext/lib/object.js":387,"polyfill/polyfill-base.js":399}],382:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":381}],383:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":381,"js-ext/extra/classes.js":385,"js-ext/lib/object.js":387}],384:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":381,"./event-emitter.js":382,"./event-listener.js":383}],385:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":387,"js-ext/extra/hashmap.js":386,"polyfill/polyfill-base.js":390}],386:[function(require,module,exports){
module.exports=require(4)
},{}],387:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":386,"polyfill/polyfill-base.js":390,"utils":391}],388:[function(require,module,exports){
module.exports=require(14)
},{}],389:[function(require,module,exports){
module.exports=require(15)
},{}],390:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":388,"./lib/window.console.js":389}],391:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":392,"./lib/timers.js":393}],392:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":386,"polyfill/polyfill-base.js":396}],393:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":396}],394:[function(require,module,exports){
module.exports=require(14)
},{}],395:[function(require,module,exports){
module.exports=require(15)
},{}],396:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":394,"./lib/window.console.js":395}],397:[function(require,module,exports){
module.exports=require(14)
},{}],398:[function(require,module,exports){
module.exports=require(15)
},{}],399:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":397,"./lib/window.console.js":398}],400:[function(require,module,exports){
module.exports=require(4)
},{}],401:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":406}],402:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":400,"polyfill/polyfill-base.js":406,"utils":407}],403:[function(require,module,exports){
module.exports=require(29)
},{}],404:[function(require,module,exports){
module.exports=require(14)
},{}],405:[function(require,module,exports){
module.exports=require(15)
},{}],406:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":404,"./lib/window.console.js":405}],407:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":408,"./lib/timers.js":409}],408:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":400,"polyfill/polyfill-base.js":412}],409:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":412}],410:[function(require,module,exports){
module.exports=require(14)
},{}],411:[function(require,module,exports){
module.exports=require(15)
},{}],412:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":410,"./lib/window.console.js":411}],413:[function(require,module,exports){
module.exports=require(14)
},{}],414:[function(require,module,exports){
module.exports=require(15)
},{}],415:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":413,"./lib/window.console.js":414}],416:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":417,"./lib/timers.js":418}],417:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":400,"polyfill/polyfill-base.js":421}],418:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":421}],419:[function(require,module,exports){
module.exports=require(14)
},{}],420:[function(require,module,exports){
module.exports=require(15)
},{}],421:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":419,"./lib/window.console.js":420}],422:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],423:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":427,"js-ext/extra/hashmap.js":424,"polyfill/polyfill-base.js":433}],424:[function(require,module,exports){
module.exports=require(4)
},{}],425:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":426,"../lib/object.js":427,"./classes.js":423,"js-ext/extra/hashmap.js":424,"polyfill/lib/weakmap.js":431}],426:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":433}],427:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":424,"polyfill/polyfill-base.js":433,"utils":434}],428:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":433}],429:[function(require,module,exports){
module.exports=require(29)
},{}],430:[function(require,module,exports){
module.exports=require(14)
},{}],431:[function(require,module,exports){
module.exports=require(57)
},{}],432:[function(require,module,exports){
module.exports=require(15)
},{}],433:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":430,"./lib/window.console.js":432}],434:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":435,"./lib/timers.js":436}],435:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":424,"polyfill/polyfill-base.js":439}],436:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":439}],437:[function(require,module,exports){
module.exports=require(14)
},{}],438:[function(require,module,exports){
module.exports=require(15)
},{}],439:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":437,"./lib/window.console.js":438}],440:[function(require,module,exports){
module.exports=require(66)
},{}],441:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":440}],442:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":440}],443:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":440}],444:[function(require,module,exports){
module.exports=require(14)
},{}],445:[function(require,module,exports){
module.exports=require(15)
},{}],446:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":444,"./lib/window.console.js":445}],447:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":448,"./lib/timers.js":449}],448:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":424,"polyfill/polyfill-base.js":452}],449:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":452}],450:[function(require,module,exports){
module.exports=require(14)
},{}],451:[function(require,module,exports){
module.exports=require(15)
},{}],452:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":450,"./lib/window.console.js":451}],453:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":454}],454:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":455,"js-ext/lib/object.js":456}],455:[function(require,module,exports){
module.exports=require(4)
},{}],456:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":455,"polyfill/polyfill-base.js":459,"utils":460}],457:[function(require,module,exports){
module.exports=require(14)
},{}],458:[function(require,module,exports){
module.exports=require(15)
},{}],459:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":457,"./lib/window.console.js":458}],460:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":461,"./lib/timers.js":462}],461:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":455,"polyfill/polyfill-base.js":465}],462:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":465}],463:[function(require,module,exports){
module.exports=require(14)
},{}],464:[function(require,module,exports){
module.exports=require(15)
},{}],465:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":463,"./lib/window.console.js":464}],466:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":424,"js-ext/lib/object.js":427,"js-ext/lib/string.js":429,"polyfill":446,"polyfill/extra/transition.js":441,"polyfill/extra/vendorCSS.js":443}],467:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":424,"js-ext/lib/object.js":427,"polyfill":446}],468:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":424,"js-ext/lib/object.js":427,"js-ext/lib/string.js":429,"polyfill":446}],469:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":422,"./attribute-extractor.js":466,"./element-array.js":467,"./html-parser.js":470,"./node-parser.js":471,"./vdom-ns.js":472,"./vnode.js":473,"js-ext/extra/hashmap.js":424,"js-ext/lib/object.js":427,"js-ext/lib/promise.js":428,"js-ext/lib/string.js":429,"polyfill":446,"polyfill/extra/transition.js":441,"polyfill/extra/transitionend.js":442,"polyfill/extra/vendorCSS.js":443,"utils":447,"window-ext":453}],470:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":466,"./vdom-ns.js":472,"js-ext/extra/hashmap.js":424,"js-ext/lib/object.js":427,"polyfill":446}],471:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":466,"./vdom-ns.js":472,"./vnode.js":473,"js-ext/extra/hashmap.js":424,"js-ext/lib/object.js":427,"polyfill":446}],472:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":424,"js-ext/lib/object.js":427,"polyfill":446}],473:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":466,"./html-parser.js":470,"./vdom-ns.js":472,"js-ext/extra/hashmap.js":424,"js-ext/extra/lightmap.js":425,"js-ext/lib/array.js":426,"js-ext/lib/object.js":427,"js-ext/lib/string.js":429,"polyfill":446,"utils/lib/timers.js":449}],474:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":468,"./partials/extend-element.js":469,"./partials/node-parser.js":471,"js-ext/extra/hashmap.js":424,"js-ext/lib/object.js":427,"utils/lib/timers.js":449}],475:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":477,"js-ext/extra/hashmap.js":476,"polyfill/polyfill-base.js":482}],476:[function(require,module,exports){
module.exports=require(4)
},{}],477:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":476,"polyfill/polyfill-base.js":482,"utils":483}],478:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":482}],479:[function(require,module,exports){
module.exports=require(29)
},{}],480:[function(require,module,exports){
module.exports=require(14)
},{}],481:[function(require,module,exports){
module.exports=require(15)
},{}],482:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":480,"./lib/window.console.js":481}],483:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":484,"./lib/timers.js":485}],484:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":476,"polyfill/polyfill-base.js":488}],485:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":488}],486:[function(require,module,exports){
module.exports=require(14)
},{}],487:[function(require,module,exports){
module.exports=require(15)
},{}],488:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":486,"./lib/window.console.js":487}],489:[function(require,module,exports){
module.exports=require(14)
},{}],490:[function(require,module,exports){
module.exports=require(15)
},{}],491:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":489,"./lib/window.console.js":490}],492:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":495}],493:[function(require,module,exports){
module.exports=require(14)
},{}],494:[function(require,module,exports){
module.exports=require(15)
},{}],495:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":493,"./lib/window.console.js":494}],496:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],497:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":501,"js-ext/extra/hashmap.js":498,"polyfill/polyfill-base.js":507}],498:[function(require,module,exports){
module.exports=require(4)
},{}],499:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":500,"../lib/object.js":501,"./classes.js":497,"js-ext/extra/hashmap.js":498,"polyfill/lib/weakmap.js":505}],500:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":507}],501:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":498,"polyfill/polyfill-base.js":507,"utils":508}],502:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":507}],503:[function(require,module,exports){
module.exports=require(29)
},{}],504:[function(require,module,exports){
module.exports=require(14)
},{}],505:[function(require,module,exports){
module.exports=require(57)
},{}],506:[function(require,module,exports){
module.exports=require(15)
},{}],507:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":504,"./lib/window.console.js":506}],508:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":509,"./lib/timers.js":510}],509:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":498,"polyfill/polyfill-base.js":513}],510:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":513}],511:[function(require,module,exports){
module.exports=require(14)
},{}],512:[function(require,module,exports){
module.exports=require(15)
},{}],513:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":511,"./lib/window.console.js":512}],514:[function(require,module,exports){
module.exports=require(66)
},{}],515:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":514}],516:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":514}],517:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":514}],518:[function(require,module,exports){
module.exports=require(14)
},{}],519:[function(require,module,exports){
module.exports=require(15)
},{}],520:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":518,"./lib/window.console.js":519}],521:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":522,"./lib/timers.js":523}],522:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":498,"polyfill/polyfill-base.js":526}],523:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":526}],524:[function(require,module,exports){
module.exports=require(14)
},{}],525:[function(require,module,exports){
module.exports=require(15)
},{}],526:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":524,"./lib/window.console.js":525}],527:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":528}],528:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":529,"js-ext/lib/object.js":530}],529:[function(require,module,exports){
module.exports=require(4)
},{}],530:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":529,"polyfill/polyfill-base.js":533,"utils":534}],531:[function(require,module,exports){
module.exports=require(14)
},{}],532:[function(require,module,exports){
module.exports=require(15)
},{}],533:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":531,"./lib/window.console.js":532}],534:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":535,"./lib/timers.js":536}],535:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":529,"polyfill/polyfill-base.js":539}],536:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":539}],537:[function(require,module,exports){
module.exports=require(14)
},{}],538:[function(require,module,exports){
module.exports=require(15)
},{}],539:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":537,"./lib/window.console.js":538}],540:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":498,"js-ext/lib/object.js":501,"js-ext/lib/string.js":503,"polyfill":520,"polyfill/extra/transition.js":515,"polyfill/extra/vendorCSS.js":517}],541:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":498,"js-ext/lib/object.js":501,"polyfill":520}],542:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":498,"js-ext/lib/object.js":501,"js-ext/lib/string.js":503,"polyfill":520}],543:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":496,"./attribute-extractor.js":540,"./element-array.js":541,"./html-parser.js":544,"./node-parser.js":545,"./vdom-ns.js":546,"./vnode.js":547,"js-ext/extra/hashmap.js":498,"js-ext/lib/object.js":501,"js-ext/lib/promise.js":502,"js-ext/lib/string.js":503,"polyfill":520,"polyfill/extra/transition.js":515,"polyfill/extra/transitionend.js":516,"polyfill/extra/vendorCSS.js":517,"utils":521,"window-ext":527}],544:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":540,"./vdom-ns.js":546,"js-ext/extra/hashmap.js":498,"js-ext/lib/object.js":501,"polyfill":520}],545:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":540,"./vdom-ns.js":546,"./vnode.js":547,"js-ext/extra/hashmap.js":498,"js-ext/lib/object.js":501,"polyfill":520}],546:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":498,"js-ext/lib/object.js":501,"polyfill":520}],547:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":540,"./html-parser.js":544,"./vdom-ns.js":546,"js-ext/extra/hashmap.js":498,"js-ext/extra/lightmap.js":499,"js-ext/lib/array.js":500,"js-ext/lib/object.js":501,"js-ext/lib/string.js":503,"polyfill":520,"utils/lib/timers.js":523}],548:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":542,"./partials/extend-element.js":543,"./partials/node-parser.js":545,"js-ext/extra/hashmap.js":498,"js-ext/lib/object.js":501,"utils/lib/timers.js":523}],549:[function(require,module,exports){
module.exports=require(14)
},{}],550:[function(require,module,exports){
module.exports=require(15)
},{}],551:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":549,"./lib/window.console.js":550}],552:[function(require,module,exports){
module.exports=require(4)
},{}],553:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":552,"polyfill/polyfill-base.js":557,"utils":558}],554:[function(require,module,exports){
module.exports=require(29)
},{}],555:[function(require,module,exports){
module.exports=require(14)
},{}],556:[function(require,module,exports){
module.exports=require(15)
},{}],557:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":555,"./lib/window.console.js":556}],558:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":559,"./lib/timers.js":560}],559:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":552,"polyfill/polyfill-base.js":563}],560:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":563}],561:[function(require,module,exports){
module.exports=require(14)
},{}],562:[function(require,module,exports){
module.exports=require(15)
},{}],563:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":561,"./lib/window.console.js":562}],564:[function(require,module,exports){
module.exports=require(14)
},{}],565:[function(require,module,exports){
module.exports=require(15)
},{}],566:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":564,"./lib/window.console.js":565}],567:[function(require,module,exports){
"use strict";
/**
*
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module useragent
* @class USERAGENT
* @since 0.0.1
*/
require('polyfill');
require('js-ext/lib/object.js');
require('js-ext/lib/string.js');
var createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
var UserAgent,
navigator = window.navigator;
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
/*jshint boss:true */
if (UserAgent=window._ITSAmodules.UserAgent) {
/*jshint boss:false */
return UserAgent; // UserAgent was already created
}
window._ITSAmodules.UserAgent = UserAgent = {
isMobile: ('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0),
isSafari: navigator.userAgent.contains('AppleWebKit')
};
return UserAgent;
};
},{"js-ext/extra/hashmap.js":552,"js-ext/lib/object.js":553,"js-ext/lib/string.js":554,"polyfill":566}],568:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],569:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":573,"js-ext/extra/hashmap.js":570,"polyfill/polyfill-base.js":579}],570:[function(require,module,exports){
module.exports=require(4)
},{}],571:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":572,"../lib/object.js":573,"./classes.js":569,"js-ext/extra/hashmap.js":570,"polyfill/lib/weakmap.js":577}],572:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":579}],573:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":570,"polyfill/polyfill-base.js":579,"utils":580}],574:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":579}],575:[function(require,module,exports){
module.exports=require(29)
},{}],576:[function(require,module,exports){
module.exports=require(14)
},{}],577:[function(require,module,exports){
module.exports=require(57)
},{}],578:[function(require,module,exports){
module.exports=require(15)
},{}],579:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":576,"./lib/window.console.js":578}],580:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":581,"./lib/timers.js":582}],581:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":570,"polyfill/polyfill-base.js":585}],582:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":585}],583:[function(require,module,exports){
module.exports=require(14)
},{}],584:[function(require,module,exports){
module.exports=require(15)
},{}],585:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":583,"./lib/window.console.js":584}],586:[function(require,module,exports){
module.exports=require(66)
},{}],587:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":586}],588:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":586}],589:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":586}],590:[function(require,module,exports){
module.exports=require(14)
},{}],591:[function(require,module,exports){
module.exports=require(15)
},{}],592:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":590,"./lib/window.console.js":591}],593:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":594,"./lib/timers.js":595}],594:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":570,"polyfill/polyfill-base.js":598}],595:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":598}],596:[function(require,module,exports){
module.exports=require(14)
},{}],597:[function(require,module,exports){
module.exports=require(15)
},{}],598:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":596,"./lib/window.console.js":597}],599:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":600}],600:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":601,"js-ext/lib/object.js":602}],601:[function(require,module,exports){
module.exports=require(4)
},{}],602:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":601,"polyfill/polyfill-base.js":605,"utils":606}],603:[function(require,module,exports){
module.exports=require(14)
},{}],604:[function(require,module,exports){
module.exports=require(15)
},{}],605:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":603,"./lib/window.console.js":604}],606:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":607,"./lib/timers.js":608}],607:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":601,"polyfill/polyfill-base.js":611}],608:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":611}],609:[function(require,module,exports){
module.exports=require(14)
},{}],610:[function(require,module,exports){
module.exports=require(15)
},{}],611:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":609,"./lib/window.console.js":610}],612:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":570,"js-ext/lib/object.js":573,"js-ext/lib/string.js":575,"polyfill":592,"polyfill/extra/transition.js":587,"polyfill/extra/vendorCSS.js":589}],613:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":570,"js-ext/lib/object.js":573,"polyfill":592}],614:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":570,"js-ext/lib/object.js":573,"js-ext/lib/string.js":575,"polyfill":592}],615:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":568,"./attribute-extractor.js":612,"./element-array.js":613,"./html-parser.js":616,"./node-parser.js":617,"./vdom-ns.js":618,"./vnode.js":619,"js-ext/extra/hashmap.js":570,"js-ext/lib/object.js":573,"js-ext/lib/promise.js":574,"js-ext/lib/string.js":575,"polyfill":592,"polyfill/extra/transition.js":587,"polyfill/extra/transitionend.js":588,"polyfill/extra/vendorCSS.js":589,"utils":593,"window-ext":599}],616:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":612,"./vdom-ns.js":618,"js-ext/extra/hashmap.js":570,"js-ext/lib/object.js":573,"polyfill":592}],617:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":612,"./vdom-ns.js":618,"./vnode.js":619,"js-ext/extra/hashmap.js":570,"js-ext/lib/object.js":573,"polyfill":592}],618:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":570,"js-ext/lib/object.js":573,"polyfill":592}],619:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":612,"./html-parser.js":616,"./vdom-ns.js":618,"js-ext/extra/hashmap.js":570,"js-ext/extra/lightmap.js":571,"js-ext/lib/array.js":572,"js-ext/lib/object.js":573,"js-ext/lib/string.js":575,"polyfill":592,"utils/lib/timers.js":595}],620:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":614,"./partials/extend-element.js":615,"./partials/node-parser.js":617,"js-ext/extra/hashmap.js":570,"js-ext/lib/object.js":573,"utils/lib/timers.js":595}],621:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":622}],622:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":623,"js-ext/lib/object.js":624}],623:[function(require,module,exports){
module.exports=require(4)
},{}],624:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":623,"polyfill/polyfill-base.js":627,"utils":628}],625:[function(require,module,exports){
module.exports=require(14)
},{}],626:[function(require,module,exports){
module.exports=require(15)
},{}],627:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":625,"./lib/window.console.js":626}],628:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":629,"./lib/timers.js":630}],629:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":623,"polyfill/polyfill-base.js":633}],630:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":633}],631:[function(require,module,exports){
module.exports=require(14)
},{}],632:[function(require,module,exports){
module.exports=require(15)
},{}],633:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":631,"./lib/window.console.js":632}],634:[function(require,module,exports){
"use strict";
/**
* Integrates mobile-events to event-dom. more about DOM-events:
* http://www.smashingmagazine.com/2013/11/12/an-introduction-to-dom-events/
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @example
* Event = require('event-mobile')(window);
*
* @module event
* @submodule event-mobile
* @class Event
* @since 0.0.1
*/
var NAME = '[event-mobile]: ';
module.exports = function (window) {
/**
* The (only) Hammer-instance that `Event` uses. It is bound to the `body`-element.
*
* @property hammertime
* @type Hammer-instance
* @since 0.0.1
*/
var Event = require('event-dom')(window),
document = window.document,
Hammer = require('./lib/hammer-2.0.4.js')(window),
hammertime = Event.hammertime = new Hammer(document.body),
singletap, doubletap, tripletap;
if (window._ITSAmodules.EventMobile) {
return Event; // Event was already extended
}
// create reference to the HammerClass:
/**
* Adds the `Hammer`-class to Event, so it can be used from within Event.
*
* @property Hammer
* @type Hammer
* @since 0.0.1
*/
Event.Hammer = Hammer;
// now we extend HammerJS with 2 events: doubletap and tripletap:
doubletap = new Hammer.Tap({ event: 'doubletap', taps: 2 });
tripletap = new Hammer.Tap({ event: 'tripletap', taps: 3 });
hammertime.add([
doubletap,
tripletap
]);
// we want to recognize this simulatenous, so a doubletap and trippletap will be detected even while a tap has been recognized.
// the tap event will be emitted on every tap
singletap = hammertime.get('tap');
doubletap.recognizeWith(singletap);
tripletap.recognizeWith([doubletap, singletap]);
// patch Hammer.Manager.prototype.emit --> it shouldn't emit to its own listeners,
// but to our eventsystem. Inspired from Jorik Tangelder's own jquery plugin: https://github.com/hammerjs/jquery.hammer.js
Hammer.Manager.prototype.emit = function(type, data) {
if (type==='hammer.input') {
return;
}
console.log(NAME, 'emit '+type);
// label the eventobject by being a Hammer-event
// is not being used internally, but we would like
// to inform the subscribers
data._isHammer = true;
data.type = type;
// Emitting 'ParcelaEvent:eventmobile' --> its defaultFn is defined inside `event-dom`
// which will transport the event through the special dom-cycle
/**
* Is emitted whenever hammerjs detects a gestureevent.
* By emitting its original event through ParcelaEvent:eventmobile, `event-dom`
* will catch it and process it through the dom-event cycle.
*
* @event ParcelaEvent:eventmobile
* @param e {Object} eventobject
* @since 0.1
**/
Event._domCallback(data);
};
Hammer.Manager.prototype.set = (function(originalSet) {
return function(options) {
delete options.domEvents; // we don't want the user make Hammer fire domevents
originalSet.call(this, options);
};
})(Hammer.Manager.prototype.set);
// store module:
window._ITSAmodules.EventMobile = Event;
return Event;
};
},{"./lib/hammer-2.0.4.js":635,"event-dom":636}],635:[function(require,module,exports){
/* Changes mad to native hammerjs:
*
* Wrapped "(function(window, DOCUMENT, exportName, undefined) {"
* is replaced by wrapped: "module.exports = function (window) {" and "return Hammer;"
*
* Created variables: DOCUMENT=window.DOCUMENT
* replaced 'DOCUMENT' by DOCUMENT furtheron in the code (ondly while word and case-sensitive)
*
* required later = require('utils').later
* Changed the function "setTimeoutContext" into using later instead of setTimeout
*/
module.exports = function (window) {
'use strict';
var DOCUMENT = window.document,
later = require('utils').later,
VENDOR_PREFIXES = ['', 'webkit', 'moz', 'MS', 'ms', 'o'],
TEST_ELEMENT = DOCUMENT.createElement('div'),
TYPE_FUNCTION = 'function',
round = Math.round,
abs = Math.abs,
now = Date.now;
/**
* set a timeout with a given scope
* @param {Function} fn
* @param {Number} timeout
* @param {Object} context
* @returns {number}
*/
function setTimeoutContext(fn, timeout, context) {
return later(bindFn(fn, context), timeout);
}
/**
* if the argument is an array, we want to execute the fn on each entry
* if it aint an array we don't want to do a thing.
* this is used by all the methods that accept a single and array argument.
* @param {*|Array} arg
* @param {String} fn
* @param {Object} [context]
* @returns {Boolean}
*/
function invokeArrayArg(arg, fn, context) {
if (Array.isArray(arg)) {
each(arg, context[fn], context);
return true;
}
return false;
}
/**
* walk objects and arrays
* @param {Object} obj
* @param {Function} iterator
* @param {Object} context
*/
function each(obj, iterator, context) {
var i;
if (!obj) {
return;
}
if (obj.forEach) {
obj.forEach(iterator, context);
} else if (obj.length !== undefined) {
i = 0;
while (i < obj.length) {
iterator.call(context, obj[i], i, obj);
i++;
}
} else {
for (i in obj) {
obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj);
}
}
}
/**
* extend object.
* means that properties in dest will be overwritten by the ones in src.
* @param {Object} dest
* @param {Object} src
* @param {Boolean} [merge]
* @returns {Object} dest
*/
function extend(dest, src, merge) {
var keys = Object.keys(src);
var i = 0;
while (i < keys.length) {
if (!merge || (merge && dest[keys[i]] === undefined)) {
dest[keys[i]] = src[keys[i]];
}
i++;
}
return dest;
}
/**
* merge the values from src in the dest.
* means that properties that exist in dest will not be overwritten by src
* @param {Object} dest
* @param {Object} src
* @returns {Object} dest
*/
function merge(dest, src) {
return extend(dest, src, true);
}
/**
* simple class inheritance
* @param {Function} child
* @param {Function} base
* @param {Object} [properties]
*/
function inherit(child, base, properties) {
var baseP = base.prototype,
childP;
childP = child.prototype = Object.create(baseP);
childP.constructor = child;
childP._super = baseP;
if (properties) {
extend(childP, properties);
}
}
/**
* simple function bind
* @param {Function} fn
* @param {Object} context
* @returns {Function}
*/
function bindFn(fn, context) {
return function boundFn() {
return fn.apply(context, arguments);
};
}
/**
* let a boolean value also be a function that must return a boolean
* this first item in args will be used as the context
* @param {Boolean|Function} val
* @param {Array} [args]
* @returns {Boolean}
*/
function boolOrFn(val, args) {
if (typeof val == TYPE_FUNCTION) {
return val.apply(args ? args[0] || undefined : undefined, args);
}
return val;
}
/**
* use the val2 when val1 is undefined
* @param {*} val1
* @param {*} val2
* @returns {*}
*/
function ifUndefined(val1, val2) {
return (val1 === undefined) ? val2 : val1;
}
/**
* addEventListener with multiple events at once
* @param {EventTarget} target
* @param {String} types
* @param {Function} handler
*/
function addEventListeners(target, types, handler) {
each(splitStr(types), function(type) {
target.addEventListener(type, handler, false);
});
}
/**
* removeEventListener with multiple events at once
* @param {EventTarget} target
* @param {String} types
* @param {Function} handler
*/
function removeEventListeners(target, types, handler) {
each(splitStr(types), function(type) {
target.removeEventListener(type, handler, false);
});
}
/**
* find if a node is in the given parent
* @method hasParent
* @param {HTMLElement} node
* @param {HTMLElement} parent
* @return {Boolean} found
*/
function hasParent(node, parent) {
while (node) {
if (node == parent) {
return true;
}
node = node.parentNode;
}
return false;
}
/**
* small indexOf wrapper
* @param {String} str
* @param {String} find
* @returns {Boolean} found
*/
function inStr(str, find) {
return str.indexOf(find) > -1;
}
/**
* split string on whitespace
* @param {String} str
* @returns {Array} words
*/
function splitStr(str) {
return str.trim().split(/\s+/g);
}
/**
* find if a array contains the object using indexOf or a simple polyFill
* @param {Array} src
* @param {String} find
* @param {String} [findByKey]
* @return {Boolean|Number} false when not found, or the index
*/
function inArray(src, find, findByKey) {
if (src.indexOf && !findByKey) {
return src.indexOf(find);
} else {
var i = 0;
while (i < src.length) {
if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) {
return i;
}
i++;
}
return -1;
}
}
/**
* convert array-like objects to real arrays
* @param {Object} obj
* @returns {Array}
*/
function toArray(obj) {
return Array.prototype.slice.call(obj, 0);
}
/**
* unique array with objects based on a key (like 'id') or just by the array's value
* @param {Array} src [{id:1},{id:2},{id:1}]
* @param {String} [key]
* @param {Boolean} [sort=False]
* @returns {Array} [{id:1},{id:2}]
*/
function uniqueArray(src, key, sort) {
var results = [];
var values = [];
var i = 0;
while (i < src.length) {
var val = key ? src[i][key] : src[i];
if (inArray(values, val) < 0) {
results.push(src[i]);
}
values[i] = val;
i++;
}
if (sort) {
if (!key) {
results = results.sort();
} else {
results = results.sort(function sortUniqueArray(a, b) {
return a[key] > b[key];
});
}
}
return results;
}
/**
* get the prefixed property
* @param {Object} obj
* @param {String} property
* @returns {String|Undefined} prefixed
*/
function prefixed(obj, property) {
var prefix, prop;
var camelProp = property[0].toUpperCase() + property.slice(1);
var i = 0;
while (i < VENDOR_PREFIXES.length) {
prefix = VENDOR_PREFIXES[i];
prop = (prefix) ? prefix + camelProp : property;
if (prop in obj) {
return prop;
}
i++;
}
return undefined;
}
/**
* get a unique id
* @returns {number} uniqueId
*/
var _uniqueId = 1;
function uniqueId() {
return _uniqueId++;
}
/**
* get the window object of an element
* @param {HTMLElement} element
* @returns {DocumentView|Window}
*/
function getWindowForElement(element) {
var doc = element.ownerDocument;
return (doc.defaultView || doc.parentWindow);
}
var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
var SUPPORT_TOUCH = ('ontouchstart' in window);
var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined;
var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(window.navigator.userAgent);
var INPUT_TYPE_TOUCH = 'touch';
var INPUT_TYPE_PEN = 'pen';
var INPUT_TYPE_MOUSE = 'mouse';
var INPUT_TYPE_KINECT = 'kinect';
var COMPUTE_INTERVAL = 25;
var INPUT_START = 1;
var INPUT_MOVE = 2;
var INPUT_END = 4;
var INPUT_CANCEL = 8;
var DIRECTION_NONE = 1;
var DIRECTION_LEFT = 2;
var DIRECTION_RIGHT = 4;
var DIRECTION_UP = 8;
var DIRECTION_DOWN = 16;
var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;
var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;
var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;
var PROPS_XY = ['x', 'y'];
var PROPS_CLIENT_XY = ['clientX', 'clientY'];
/**
* create new input type manager
* @param {Manager} manager
* @param {Function} callback
* @returns {Input}
* @constructor
*/
function Input(manager, callback) {
var self = this;
this.manager = manager;
this.callback = callback;
this.element = manager.element;
this.target = manager.options.inputTarget;
// smaller wrapper around the handler, for the scope and the enabled state of the manager,
// so when disabled the input events are completely bypassed.
this.domHandler = function(ev) {
if (boolOrFn(manager.options.enable, [manager])) {
self.handler(ev);
}
};
this.init();
}
Input.prototype = {
/**
* should handle the inputEvent data and trigger the callback
* @virtual
*/
handler: function() { },
/**
* bind the events
*/
init: function() {
this.evEl && addEventListeners(this.element, this.evEl, this.domHandler);
this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler);
this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
},
/**
* unbind the events
*/
destroy: function() {
this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler);
this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler);
this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
}
};
/**
* create new input type manager
* called by the Manager constructor
* @param {Hammer} manager
* @returns {Input}
*/
function createInputInstance(manager) {
var Type;
var inputClass = manager.options.inputClass;
if (inputClass) {
Type = inputClass;
} else if (SUPPORT_POINTER_EVENTS) {
Type = PointerEventInput;
} else if (SUPPORT_ONLY_TOUCH) {
Type = TouchInput;
} else if (!SUPPORT_TOUCH) {
Type = MouseInput;
} else {
Type = TouchMouseInput;
}
return new (Type)(manager, inputHandler);
}
/**
* handle input events
* @param {Manager} manager
* @param {String} eventType
* @param {Object} input
*/
function inputHandler(manager, eventType, input) {
var pointersLen = input.pointers.length;
var changedPointersLen = input.changedPointers.length;
var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0));
var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0));
input.isFirst = !!isFirst;
input.isFinal = !!isFinal;
if (isFirst) {
manager.session = {};
}
// source event is the normalized value of the domEvents
// like 'touchstart, mouseup, pointerdown'
input.eventType = eventType;
// compute scale, rotation etc
computeInputData(manager, input);
// emit secret event
manager.emit('hammer.input', input);
manager.recognize(input);
manager.session.prevInput = input;
}
/**
* extend the data with some usable properties like scale, rotate, velocity etc
* @param {Object} manager
* @param {Object} input
*/
function computeInputData(manager, input) {
var session = manager.session;
var pointers = input.pointers;
var pointersLength = pointers.length;
// store the first input to calculate the distance and direction
if (!session.firstInput) {
session.firstInput = simpleCloneInputData(input);
}
// to compute scale and rotation we need to store the multiple touches
if (pointersLength > 1 && !session.firstMultiple) {
session.firstMultiple = simpleCloneInputData(input);
} else if (pointersLength === 1) {
session.firstMultiple = false;
}
var firstInput = session.firstInput;
var firstMultiple = session.firstMultiple;
var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center;
var center = input.center = getCenter(pointers);
input.timeStamp = now();
input.deltaTime = input.timeStamp - firstInput.timeStamp;
input.angle = getAngle(offsetCenter, center);
input.distance = getDistance(offsetCenter, center);
computeDeltaXY(session, input);
input.offsetDirection = getDirection(input.deltaX, input.deltaY);
input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1;
input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0;
computeIntervalInputData(session, input);
// find the correct target
var target = manager.element;
if (hasParent(input.srcEvent.target, target)) {
target = input.srcEvent.target;
}
input.target = target;
}
function computeDeltaXY(session, input) {
var center = input.center;
var offset = session.offsetDelta || {};
var prevDelta = session.prevDelta || {};
var prevInput = session.prevInput || {};
if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) {
prevDelta = session.prevDelta = {
x: prevInput.deltaX || 0,
y: prevInput.deltaY || 0
};
offset = session.offsetDelta = {
x: center.x,
y: center.y
};
}
input.deltaX = prevDelta.x + (center.x - offset.x);
input.deltaY = prevDelta.y + (center.y - offset.y);
}
/**
* velocity is calculated every x ms
* @param {Object} session
* @param {Object} input
*/
function computeIntervalInputData(session, input) {
var last = session.lastInterval || input,
deltaTime = input.timeStamp - last.timeStamp,
velocity, velocityX, velocityY, direction;
if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) {
var deltaX = last.deltaX - input.deltaX;
var deltaY = last.deltaY - input.deltaY;
var v = getVelocity(deltaTime, deltaX, deltaY);
velocityX = v.x;
velocityY = v.y;
velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y;
direction = getDirection(deltaX, deltaY);
session.lastInterval = input;
} else {
// use latest velocity info if it doesn't overtake a minimum period
velocity = last.velocity;
velocityX = last.velocityX;
velocityY = last.velocityY;
direction = last.direction;
}
input.velocity = velocity;
input.velocityX = velocityX;
input.velocityY = velocityY;
input.direction = direction;
}
/**
* create a simple clone from the input used for storage of firstInput and firstMultiple
* @param {Object} input
* @returns {Object} clonedInputData
*/
function simpleCloneInputData(input) {
// make a simple copy of the pointers because we will get a reference if we don't
// we only need clientXY for the calculations
var pointers = [];
var i = 0;
while (i < input.pointers.length) {
pointers[i] = {
clientX: round(input.pointers[i].clientX),
clientY: round(input.pointers[i].clientY)
};
i++;
}
return {
timeStamp: now(),
pointers: pointers,
center: getCenter(pointers),
deltaX: input.deltaX,
deltaY: input.deltaY
};
}
/**
* get the center of all the pointers
* @param {Array} pointers
* @return {Object} center contains `x` and `y` properties
*/
function getCenter(pointers) {
var pointersLength = pointers.length;
// no need to loop when only one touch
if (pointersLength === 1) {
return {
x: round(pointers[0].clientX),
y: round(pointers[0].clientY)
};
}
var x = 0,
y = 0,
i = 0;
while (i < pointersLength) {
x += pointers[i].clientX;
y += pointers[i].clientY;
i++;
}
return {
x: round(x / pointersLength),
y: round(y / pointersLength)
};
}
/**
* calculate the velocity between two points. unit is in px per ms.
* @param {Number} deltaTime
* @param {Number} x
* @param {Number} y
* @return {Object} velocity `x` and `y`
*/
function getVelocity(deltaTime, x, y) {
return {
x: x / deltaTime || 0,
y: y / deltaTime || 0
};
}
/**
* get the direction between two points
* @param {Number} x
* @param {Number} y
* @return {Number} direction
*/
function getDirection(x, y) {
if (x === y) {
return DIRECTION_NONE;
}
if (abs(x) >= abs(y)) {
return x > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
}
return y > 0 ? DIRECTION_UP : DIRECTION_DOWN;
}
/**
* calculate the absolute distance between two points
* @param {Object} p1 {x, y}
* @param {Object} p2 {x, y}
* @param {Array} [props] containing x and y keys
* @return {Number} distance
*/
function getDistance(p1, p2, props) {
if (!props) {
props = PROPS_XY;
}
var x = p2[props[0]] - p1[props[0]],
y = p2[props[1]] - p1[props[1]];
return Math.sqrt((x * x) + (y * y));
}
/**
* calculate the angle between two coordinates
* @param {Object} p1
* @param {Object} p2
* @param {Array} [props] containing x and y keys
* @return {Number} angle
*/
function getAngle(p1, p2, props) {
if (!props) {
props = PROPS_XY;
}
var x = p2[props[0]] - p1[props[0]],
y = p2[props[1]] - p1[props[1]];
return Math.atan2(y, x) * 180 / Math.PI;
}
/**
* calculate the rotation degrees between two pointersets
* @param {Array} start array of pointers
* @param {Array} end array of pointers
* @return {Number} rotation
*/
function getRotation(start, end) {
return getAngle(end[1], end[0], PROPS_CLIENT_XY) - getAngle(start[1], start[0], PROPS_CLIENT_XY);
}
/**
* calculate the scale factor between two pointersets
* no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
* @param {Array} start array of pointers
* @param {Array} end array of pointers
* @return {Number} scale
*/
function getScale(start, end) {
return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY);
}
var MOUSE_INPUT_MAP = {
mousedown: INPUT_START,
mousemove: INPUT_MOVE,
mouseup: INPUT_END
};
var MOUSE_ELEMENT_EVENTS = 'mousedown';
var MOUSE_WINDOW_EVENTS = 'mousemove mouseup';
/**
* Mouse events input
* @constructor
* @extends Input
*/
function MouseInput() {
this.evEl = MOUSE_ELEMENT_EVENTS;
this.evWin = MOUSE_WINDOW_EVENTS;
this.allow = true; // used by Input.TouchMouse to disable mouse events
this.pressed = false; // mousedown state
Input.apply(this, arguments);
}
inherit(MouseInput, Input, {
/**
* handle mouse events
* @param {Object} ev
*/
handler: function MEhandler(ev) {
var eventType = MOUSE_INPUT_MAP[ev.type];
// on start we want to have the left mouse button down
if (eventType & INPUT_START && ev.button === 0) {
this.pressed = true;
}
if (eventType & INPUT_MOVE && ev.which !== 1) {
eventType = INPUT_END;
}
// mouse must be down, and mouse events are allowed (see the TouchMouse input)
if (!this.pressed || !this.allow) {
return;
}
if (eventType & INPUT_END) {
this.pressed = false;
}
this.callback(this.manager, eventType, {
pointers: [ev],
changedPointers: [ev],
pointerType: INPUT_TYPE_MOUSE,
srcEvent: ev
});
}
});
var POINTER_INPUT_MAP = {
pointerdown: INPUT_START,
pointermove: INPUT_MOVE,
pointerup: INPUT_END,
pointercancel: INPUT_CANCEL,
pointerout: INPUT_CANCEL
};
// in IE10 the pointer types is defined as an enum
var IE10_POINTER_TYPE_ENUM = {
2: INPUT_TYPE_TOUCH,
3: INPUT_TYPE_PEN,
4: INPUT_TYPE_MOUSE,
5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816
};
var POINTER_ELEMENT_EVENTS = 'pointerdown';
var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel';
// IE10 has prefixed support, and case-sensitive
if (window.MSPointerEvent) {
POINTER_ELEMENT_EVENTS = 'MSPointerDown';
POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel';
}
/**
* Pointer events input
* @constructor
* @extends Input
*/
function PointerEventInput() {
this.evEl = POINTER_ELEMENT_EVENTS;
this.evWin = POINTER_WINDOW_EVENTS;
Input.apply(this, arguments);
this.store = (this.manager.session.pointerEvents = []);
}
inherit(PointerEventInput, Input, {
/**
* handle mouse events
* @param {Object} ev
*/
handler: function PEhandler(ev) {
var store = this.store;
var removePointer = false;
var eventTypeNormalized = ev.type.toLowerCase().replace('ms', '');
var eventType = POINTER_INPUT_MAP[eventTypeNormalized];
var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType;
var isTouch = (pointerType == INPUT_TYPE_TOUCH);
// start and mouse must be down
if (eventType & INPUT_START && (ev.button === 0 || isTouch)) {
store.push(ev);
} else if (eventType & (INPUT_END | INPUT_CANCEL)) {
removePointer = true;
}
// get index of the event in the store
// it not found, so the pointer hasn't been down (so it's probably a hover)
var storeIndex = inArray(store, ev.pointerId, 'pointerId');
if (storeIndex < 0) {
return;
}
// update the event in the store
store[storeIndex] = ev;
this.callback(this.manager, eventType, {
pointers: store,
changedPointers: [ev],
pointerType: pointerType,
srcEvent: ev
});
if (removePointer) {
// remove from the store
store.splice(storeIndex, 1);
}
}
});
var TOUCH_INPUT_MAP = {
touchstart: INPUT_START,
touchmove: INPUT_MOVE,
touchend: INPUT_END,
touchcancel: INPUT_CANCEL
};
var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel';
/**
* Touch events input
* @constructor
* @extends Input
*/
function TouchInput() {
this.evTarget = TOUCH_TARGET_EVENTS;
this.targetIds = {};
Input.apply(this, arguments);
}
inherit(TouchInput, Input, {
/**
* handle touch events
* @param {Object} ev
*/
handler: function TEhandler(ev) {
var type = TOUCH_INPUT_MAP[ev.type];
var touches = getTouches.call(this, ev, type);
if (!touches) {
return;
}
this.callback(this.manager, type, {
pointers: touches[0],
changedPointers: touches[1],
pointerType: INPUT_TYPE_TOUCH,
srcEvent: ev
});
}
});
/**
* @this {TouchInput}
* @param {Object} ev
* @param {Number} type flag
* @returns {undefined|Array} [all, changed]
*/
function getTouches(ev, type) {
var allTouches = toArray(ev.touches);
var targetIds = this.targetIds;
// when there is only one touch, the process can be simplified
if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) {
targetIds[allTouches[0].identifier] = true;
return [allTouches, allTouches];
}
var i,
targetTouches = toArray(ev.targetTouches),
changedTouches = toArray(ev.changedTouches),
changedTargetTouches = [];
// collect touches
if (type === INPUT_START) {
i = 0;
while (i < targetTouches.length) {
targetIds[targetTouches[i].identifier] = true;
i++;
}
}
// filter changed touches to only contain touches that exist in the collected target ids
i = 0;
while (i < changedTouches.length) {
if (targetIds[changedTouches[i].identifier]) {
changedTargetTouches.push(changedTouches[i]);
}
// cleanup removed touches
if (type & (INPUT_END | INPUT_CANCEL)) {
delete targetIds[changedTouches[i].identifier];
}
i++;
}
if (!changedTargetTouches.length) {
return;
}
return [
// merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel'
uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true),
changedTargetTouches
];
}
/**
* Combined touch and mouse input
*
* Touch has a higher priority then mouse, and while touching no mouse events are allowed.
* This because touch devices also emit mouse events while doing a touch.
*
* @constructor
* @extends Input
*/
function TouchMouseInput() {
Input.apply(this, arguments);
var handler = bindFn(this.handler, this);
this.touch = new TouchInput(this.manager, handler);
this.mouse = new MouseInput(this.manager, handler);
}
inherit(TouchMouseInput, Input, {
/**
* handle mouse and touch events
* @param {Hammer} manager
* @param {String} inputEvent
* @param {Object} inputData
*/
handler: function TMEhandler(manager, inputEvent, inputData) {
var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH),
isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE);
// when we're in a touch event, so block all upcoming mouse events
// most mobile browser also emit mouseevents, right after touchstart
if (isTouch) {
this.mouse.allow = false;
} else if (isMouse && !this.mouse.allow) {
return;
}
// reset the allowMouse when we're done
if (inputEvent & (INPUT_END | INPUT_CANCEL)) {
this.mouse.allow = true;
}
this.callback(manager, inputEvent, inputData);
},
/**
* remove the event listeners
*/
destroy: function destroy() {
this.touch.destroy();
this.mouse.destroy();
}
});
var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction');
var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined;
// magical touchAction value
var TOUCH_ACTION_COMPUTE = 'compute';
var TOUCH_ACTION_AUTO = 'auto';
var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented
var TOUCH_ACTION_NONE = 'none';
var TOUCH_ACTION_PAN_X = 'pan-x';
var TOUCH_ACTION_PAN_Y = 'pan-y';
/**
* Touch Action
* sets the touchAction property or uses the js alternative
* @param {Manager} manager
* @param {String} value
* @constructor
*/
function TouchAction(manager, value) {
this.manager = manager;
this.set(value);
}
TouchAction.prototype = {
/**
* set the touchAction value on the element or enable the polyfill
* @param {String} value
*/
set: function(value) {
// find out the touch-action by the event handlers
if (value == TOUCH_ACTION_COMPUTE) {
value = this.compute();
}
if (NATIVE_TOUCH_ACTION) {
this.manager.element.style[PREFIXED_TOUCH_ACTION] = value;
}
this.actions = value.toLowerCase().trim();
},
/**
* just re-set the touchAction value
*/
update: function() {
this.set(this.manager.options.touchAction);
},
/**
* compute the value for the touchAction property based on the recognizer's settings
* @returns {String} value
*/
compute: function() {
var actions = [];
each(this.manager.recognizers, function(recognizer) {
if (boolOrFn(recognizer.options.enable, [recognizer])) {
actions = actions.concat(recognizer.getTouchAction());
}
});
return cleanTouchActions(actions.join(' '));
},
/**
* this method is called on each input cycle and provides the preventing of the browser behavior
* @param {Object} input
*/
preventDefaults: function(input) {
// not needed with native support for the touchAction property
if (NATIVE_TOUCH_ACTION) {
return;
}
var srcEvent = input.srcEvent;
var direction = input.offsetDirection;
// if the touch action did prevented once this session
if (this.manager.session.prevented) {
srcEvent.preventDefault();
return;
}
var actions = this.actions;
var hasNone = inStr(actions, TOUCH_ACTION_NONE);
var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
if (hasNone ||
(hasPanY && direction & DIRECTION_HORIZONTAL) ||
(hasPanX && direction & DIRECTION_VERTICAL)) {
return this.preventSrc(srcEvent);
}
},
/**
* call preventDefault to prevent the browser's default behavior (scrolling in most cases)
* @param {Object} srcEvent
*/
preventSrc: function(srcEvent) {
this.manager.session.prevented = true;
srcEvent.preventDefault();
}
};
/**
* when the touchActions are collected they are not a valid value, so we need to clean things up. *
* @param {String} actions
* @returns {*}
*/
function cleanTouchActions(actions) {
// none
if (inStr(actions, TOUCH_ACTION_NONE)) {
return TOUCH_ACTION_NONE;
}
var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
// pan-x and pan-y can be combined
if (hasPanX && hasPanY) {
return TOUCH_ACTION_PAN_X + ' ' + TOUCH_ACTION_PAN_Y;
}
// pan-x OR pan-y
if (hasPanX || hasPanY) {
return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y;
}
// manipulation
if (inStr(actions, TOUCH_ACTION_MANIPULATION)) {
return TOUCH_ACTION_MANIPULATION;
}
return TOUCH_ACTION_AUTO;
}
/**
* Recognizer flow explained; *
* All recognizers have the initial state of POSSIBLE when a input session starts.
* The definition of a input session is from the first input until the last input, with all it's movement in it. *
* Example session for mouse-input: mousedown -> mousemove -> mouseup
*
* On each recognizing cycle (see Manager.recognize) the .recognize() method is executed
* which determines with state it should be.
*
* If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to
* POSSIBLE to give it another change on the next cycle.
*
* Possible
* |
* +-----+---------------+
* | |
* +-----+-----+ |
* | | |
* Failed Cancelled |
* +-------+------+
* | |
* Recognized Began
* |
* Changed
* |
* Ended/Recognized
*/
var STATE_POSSIBLE = 1;
var STATE_BEGAN = 2;
var STATE_CHANGED = 4;
var STATE_ENDED = 8;
var STATE_RECOGNIZED = STATE_ENDED;
var STATE_CANCELLED = 16;
var STATE_FAILED = 32;
/**
* Recognizer
* Every recognizer needs to extend from this class.
* @constructor
* @param {Object} options
*/
function Recognizer(options) {
this.id = uniqueId();
this.manager = null;
this.options = merge(options || {}, this.defaults);
// default is enable true
this.options.enable = ifUndefined(this.options.enable, true);
this.state = STATE_POSSIBLE;
this.simultaneous = {};
this.requireFail = [];
}
Recognizer.prototype = {
/**
* @virtual
* @type {Object}
*/
defaults: {},
/**
* set options
* @param {Object} options
* @return {Recognizer}
*/
set: function(options) {
extend(this.options, options);
// also update the touchAction, in case something changed about the directions/enabled state
this.manager && this.manager.touchAction.update();
return this;
},
/**
* recognize simultaneous with an other recognizer.
* @param {Recognizer} otherRecognizer
* @returns {Recognizer} this
*/
recognizeWith: function(otherRecognizer) {
if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) {
return this;
}
var simultaneous = this.simultaneous;
otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
if (!simultaneous[otherRecognizer.id]) {
simultaneous[otherRecognizer.id] = otherRecognizer;
otherRecognizer.recognizeWith(this);
}
return this;
},
/**
* drop the simultaneous link. it doesnt remove the link on the other recognizer.
* @param {Recognizer} otherRecognizer
* @returns {Recognizer} this
*/
dropRecognizeWith: function(otherRecognizer) {
if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) {
return this;
}
otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
delete this.simultaneous[otherRecognizer.id];
return this;
},
/**
* recognizer can only run when an other is failing
* @param {Recognizer} otherRecognizer
* @returns {Recognizer} this
*/
requireFailure: function(otherRecognizer) {
if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) {
return this;
}
var requireFail = this.requireFail;
otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
if (inArray(requireFail, otherRecognizer) === -1) {
requireFail.push(otherRecognizer);
otherRecognizer.requireFailure(this);
}
return this;
},
/**
* drop the requireFailure link. it does not remove the link on the other recognizer.
* @param {Recognizer} otherRecognizer
* @returns {Recognizer} this
*/
dropRequireFailure: function(otherRecognizer) {
if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) {
return this;
}
otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
var index = inArray(this.requireFail, otherRecognizer);
if (index > -1) {
this.requireFail.splice(index, 1);
}
return this;
},
/**
* has require failures boolean
* @returns {boolean}
*/
hasRequireFailures: function() {
return this.requireFail.length > 0;
},
/**
* if the recognizer can recognize simultaneous with an other recognizer
* @param {Recognizer} otherRecognizer
* @returns {Boolean}
*/
canRecognizeWith: function(otherRecognizer) {
return !!this.simultaneous[otherRecognizer.id];
},
/**
* You should use `tryEmit` instead of `emit` directly to check
* that all the needed recognizers has failed before emitting.
* @param {Object} input
*/
emit: function(input) {
var self = this;
var state = this.state;
function emit(withState) {
self.manager.emit(self.options.event + (withState ? stateStr(state) : ''), input);
}
// 'panstart' and 'panmove'
if (state < STATE_ENDED) {
emit(true);
}
emit(); // simple 'eventName' events
// panend and pancancel
if (state >= STATE_ENDED) {
emit(true);
}
},
/**
* Check that all the require failure recognizers has failed,
* if true, it emits a gesture event,
* otherwise, setup the state to FAILED.
* @param {Object} input
*/
tryEmit: function(input) {
if (this.canEmit()) {
return this.emit(input);
}
// it's failing anyway
this.state = STATE_FAILED;
},
/**
* can we emit?
* @returns {boolean}
*/
canEmit: function() {
var i = 0;
while (i < this.requireFail.length) {
if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) {
return false;
}
i++;
}
return true;
},
/**
* update the recognizer
* @param {Object} inputData
*/
recognize: function(inputData) {
// make a new copy of the inputData
// so we can change the inputData without messing up the other recognizers
var inputDataClone = extend({}, inputData);
// is is enabled and allow recognizing?
if (!boolOrFn(this.options.enable, [this, inputDataClone])) {
this.reset();
this.state = STATE_FAILED;
return;
}
// reset when we've reached the end
if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) {
this.state = STATE_POSSIBLE;
}
this.state = this.process(inputDataClone);
// the recognizer has recognized a gesture
// so trigger an event
if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) {
this.tryEmit(inputDataClone);
}
},
/**
* return the state of the recognizer
* the actual recognizing happens in this method
* @virtual
* @param {Object} inputData
* @returns {Const} STATE
*/
process: function(inputData) { }, // jshint ignore:line
/**
* return the preferred touch-action
* @virtual
* @returns {Array}
*/
getTouchAction: function() { },
/**
* called when the gesture isn't allowed to recognize
* like when another is being recognized or it is disabled
* @virtual
*/
reset: function() { }
};
/**
* get a usable string, used as event postfix
* @param {Const} state
* @returns {String} state
*/
function stateStr(state) {
if (state & STATE_CANCELLED) {
return 'cancel';
} else if (state & STATE_ENDED) {
return 'end';
} else if (state & STATE_CHANGED) {
return 'move';
} else if (state & STATE_BEGAN) {
return 'start';
}
return '';
}
/**
* direction cons to string
* @param {Const} direction
* @returns {String}
*/
function directionStr(direction) {
if (direction == DIRECTION_DOWN) {
return 'down';
} else if (direction == DIRECTION_UP) {
return 'up';
} else if (direction == DIRECTION_LEFT) {
return 'left';
} else if (direction == DIRECTION_RIGHT) {
return 'right';
}
return '';
}
/**
* get a recognizer by name if it is bound to a manager
* @param {Recognizer|String} otherRecognizer
* @param {Recognizer} recognizer
* @returns {Recognizer}
*/
function getRecognizerByNameIfManager(otherRecognizer, recognizer) {
var manager = recognizer.manager;
if (manager) {
return manager.get(otherRecognizer);
}
return otherRecognizer;
}
/**
* This recognizer is just used as a base for the simple attribute recognizers.
* @constructor
* @extends Recognizer
*/
function AttrRecognizer() {
Recognizer.apply(this, arguments);
}
inherit(AttrRecognizer, Recognizer, {
/**
* @namespace
* @memberof AttrRecognizer
*/
defaults: {
/**
* @type {Number}
* @default 1
*/
pointers: 1
},
/**
* Used to check if it the recognizer receives valid input, like input.distance > 10.
* @memberof AttrRecognizer
* @param {Object} input
* @returns {Boolean} recognized
*/
attrTest: function(input) {
var optionPointers = this.options.pointers;
return optionPointers === 0 || input.pointers.length === optionPointers;
},
/**
* Process the input and return the state for the recognizer
* @memberof AttrRecognizer
* @param {Object} input
* @returns {*} State
*/
process: function(input) {
var state = this.state;
var eventType = input.eventType;
var isRecognized = state & (STATE_BEGAN | STATE_CHANGED);
var isValid = this.attrTest(input);
// on cancel input and we've recognized before, return STATE_CANCELLED
if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) {
return state | STATE_CANCELLED;
} else if (isRecognized || isValid) {
if (eventType & INPUT_END) {
return state | STATE_ENDED;
} else if (!(state & STATE_BEGAN)) {
return STATE_BEGAN;
}
return state | STATE_CHANGED;
}
return STATE_FAILED;
}
});
/**
* Pan
* Recognized when the pointer is down and moved in the allowed direction.
* @constructor
* @extends AttrRecognizer
*/
function PanRecognizer() {
AttrRecognizer.apply(this, arguments);
this.pX = null;
this.pY = null;
}
inherit(PanRecognizer, AttrRecognizer, {
/**
* @namespace
* @memberof PanRecognizer
*/
defaults: {
event: 'pan',
threshold: 10,
pointers: 1,
direction: DIRECTION_ALL
},
getTouchAction: function() {
var direction = this.options.direction;
var actions = [];
if (direction & DIRECTION_HORIZONTAL) {
actions.push(TOUCH_ACTION_PAN_Y);
}
if (direction & DIRECTION_VERTICAL) {
actions.push(TOUCH_ACTION_PAN_X);
}
return actions;
},
directionTest: function(input) {
var options = this.options;
var hasMoved = true;
var distance = input.distance;
var direction = input.direction;
var x = input.deltaX;
var y = input.deltaY;
// lock to axis?
if (!(direction & options.direction)) {
if (options.direction & DIRECTION_HORIZONTAL) {
direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT;
hasMoved = x != this.pX;
distance = Math.abs(input.deltaX);
} else {
direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN;
hasMoved = y != this.pY;
distance = Math.abs(input.deltaY);
}
}
input.direction = direction;
return hasMoved && distance > options.threshold && direction & options.direction;
},
attrTest: function(input) {
return AttrRecognizer.prototype.attrTest.call(this, input) &&
(this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input)));
},
emit: function(input) {
this.pX = input.deltaX;
this.pY = input.deltaY;
var direction = directionStr(input.direction);
if (direction) {
this.manager.emit(this.options.event + direction, input);
}
this._super.emit.call(this, input);
}
});
/**
* Pinch
* Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out).
* @constructor
* @extends AttrRecognizer
*/
function PinchRecognizer() {
AttrRecognizer.apply(this, arguments);
}
inherit(PinchRecognizer, AttrRecognizer, {
/**
* @namespace
* @memberof PinchRecognizer
*/
defaults: {
event: 'pinch',
threshold: 0,
pointers: 2
},
getTouchAction: function() {
return [TOUCH_ACTION_NONE];
},
attrTest: function(input) {
return this._super.attrTest.call(this, input) &&
(Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN);
},
emit: function(input) {
this._super.emit.call(this, input);
if (input.scale !== 1) {
var inOut = input.scale < 1 ? 'in' : 'out';
this.manager.emit(this.options.event + inOut, input);
}
}
});
/**
* Press
* Recognized when the pointer is down for x ms without any movement.
* @constructor
* @extends Recognizer
*/
function PressRecognizer() {
Recognizer.apply(this, arguments);
this._timer = null;
this._input = null;
}
inherit(PressRecognizer, Recognizer, {
/**
* @namespace
* @memberof PressRecognizer
*/
defaults: {
event: 'press',
pointers: 1,
time: 500, // minimal time of the pointer to be pressed
threshold: 5 // a minimal movement is ok, but keep it low
},
getTouchAction: function() {
return [TOUCH_ACTION_AUTO];
},
process: function(input) {
var options = this.options;
var validPointers = input.pointers.length === options.pointers;
var validMovement = input.distance < options.threshold;
var validTime = input.deltaTime > options.time;
this._input = input;
// we only allow little movement
// and we've reached an end event, so a tap is possible
if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) {
this.reset();
} else if (input.eventType & INPUT_START) {
this.reset();
this._timer = setTimeoutContext(function() {
this.state = STATE_RECOGNIZED;
this.tryEmit();
}, options.time, this);
} else if (input.eventType & INPUT_END) {
return STATE_RECOGNIZED;
}
return STATE_FAILED;
},
reset: function() {
clearTimeout(this._timer);
},
emit: function(input) {
if (this.state !== STATE_RECOGNIZED) {
return;
}
if (input && (input.eventType & INPUT_END)) {
this.manager.emit(this.options.event + 'up', input);
} else {
this._input.timeStamp = now();
this.manager.emit(this.options.event, this._input);
}
}
});
/**
* Rotate
* Recognized when two or more pointer are moving in a circular motion.
* @constructor
* @extends AttrRecognizer
*/
function RotateRecognizer() {
AttrRecognizer.apply(this, arguments);
}
inherit(RotateRecognizer, AttrRecognizer, {
/**
* @namespace
* @memberof RotateRecognizer
*/
defaults: {
event: 'rotate',
threshold: 0,
pointers: 2
},
getTouchAction: function() {
return [TOUCH_ACTION_NONE];
},
attrTest: function(input) {
return this._super.attrTest.call(this, input) &&
(Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN);
}
});
/**
* Swipe
* Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction.
* @constructor
* @extends AttrRecognizer
*/
function SwipeRecognizer() {
AttrRecognizer.apply(this, arguments);
}
inherit(SwipeRecognizer, AttrRecognizer, {
/**
* @namespace
* @memberof SwipeRecognizer
*/
defaults: {
event: 'swipe',
threshold: 10,
velocity: 0.65,
direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL,
pointers: 1
},
getTouchAction: function() {
return PanRecognizer.prototype.getTouchAction.call(this);
},
attrTest: function(input) {
var direction = this.options.direction;
var velocity;
if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) {
velocity = input.velocity;
} else if (direction & DIRECTION_HORIZONTAL) {
velocity = input.velocityX;
} else if (direction & DIRECTION_VERTICAL) {
velocity = input.velocityY;
}
return this._super.attrTest.call(this, input) &&
direction & input.direction &&
input.distance > this.options.threshold &&
abs(velocity) > this.options.velocity && input.eventType & INPUT_END;
},
emit: function(input) {
var direction = directionStr(input.direction);
if (direction) {
this.manager.emit(this.options.event + direction, input);
}
this.manager.emit(this.options.event, input);
}
});
/**
* A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur
* between the given interval and position. The delay option can be used to recognize multi-taps without firing
* a single tap.
*
* The eventData from the emitted event contains the property `tapCount`, which contains the amount of
* multi-taps being recognized.
* @constructor
* @extends Recognizer
*/
function TapRecognizer() {
Recognizer.apply(this, arguments);
// previous time and center,
// used for tap counting
this.pTime = false;
this.pCenter = false;
this._timer = null;
this._input = null;
this.count = 0;
}
inherit(TapRecognizer, Recognizer, {
/**
* @namespace
* @memberof PinchRecognizer
*/
defaults: {
event: 'tap',
pointers: 1,
taps: 1,
interval: 300, // max time between the multi-tap taps
time: 250, // max time of the pointer to be down (like finger on the screen)
threshold: 2, // a minimal movement is ok, but keep it low
posThreshold: 10 // a multi-tap can be a bit off the initial position
},
getTouchAction: function() {
return [TOUCH_ACTION_MANIPULATION];
},
process: function(input) {
var options = this.options;
var validPointers = input.pointers.length === options.pointers;
var validMovement = input.distance < options.threshold;
var validTouchTime = input.deltaTime < options.time;
this.reset();
if ((input.eventType & INPUT_START) && (this.count === 0)) {
return this.failTimeout();
}
// we only allow little movement
// and we've reached an end event, so a tap is possible
if (validMovement && validTouchTime && validPointers) {
if (input.eventType != INPUT_END) {
return this.failTimeout();
}
var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true;
var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold;
this.pTime = input.timeStamp;
this.pCenter = input.center;
if (!validMultiTap || !validInterval) {
this.count = 1;
} else {
this.count += 1;
}
this._input = input;
// if tap count matches we have recognized it,
// else it has began recognizing...
var tapCount = this.count % options.taps;
if (tapCount === 0) {
// no failing requirements, immediately trigger the tap event
// or wait as long as the multitap interval to trigger
if (!this.hasRequireFailures()) {
return STATE_RECOGNIZED;
} else {
this._timer = setTimeoutContext(function() {
this.state = STATE_RECOGNIZED;
this.tryEmit();
}, options.interval, this);
return STATE_BEGAN;
}
}
}
return STATE_FAILED;
},
failTimeout: function() {
this._timer = setTimeoutContext(function() {
this.state = STATE_FAILED;
}, this.options.interval, this);
return STATE_FAILED;
},
reset: function() {
clearTimeout(this._timer);
},
emit: function() {
if (this.state == STATE_RECOGNIZED ) {
this._input.tapCount = this.count;
this.manager.emit(this.options.event, this._input);
}
}
});
/**
* Simple way to create an manager with a default set of recognizers.
* @param {HTMLElement} element
* @param {Object} [options]
* @constructor
*/
function Hammer(element, options) {
options = options || {};
options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset);
return new Manager(element, options);
}
/**
* @const {string}
*/
Hammer.VERSION = '2.0.3';
/**
* default settings
* @namespace
*/
Hammer.defaults = {
/**
* set if DOM events are being triggered.
* But this is slower and unused by simple implementations, so disabled by default.
* @type {Boolean}
* @default false
*/
domEvents: false,
/**
* The value for the touchAction property/fallback.
* When set to `compute` it will magically set the correct value based on the added recognizers.
* @type {String}
* @default compute
*/
touchAction: TOUCH_ACTION_COMPUTE,
/**
* @type {Boolean}
* @default true
*/
enable: true,
/**
* EXPERIMENTAL FEATURE -- can be removed/changed
* Change the parent input target element.
* If Null, then it is being set the to main element.
* @type {Null|EventTarget}
* @default null
*/
inputTarget: null,
/**
* force an input class
* @type {Null|Function}
* @default null
*/
inputClass: null,
/**
* Default recognizer setup when calling `Hammer()`
* When creating a new Manager these will be skipped.
* @type {Array}
*/
preset: [
// RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...]
[RotateRecognizer, { enable: false }],
[PinchRecognizer, { enable: false }, ['rotate']],
[SwipeRecognizer,{ direction: DIRECTION_HORIZONTAL }],
[PanRecognizer, { direction: DIRECTION_HORIZONTAL }, ['swipe']],
[TapRecognizer],
[TapRecognizer, { event: 'doubletap', taps: 2 }, ['tap']],
[PressRecognizer]
],
/**
* Some CSS properties can be used to improve the working of Hammer.
* Add them to this method and they will be set when creating a new Manager.
* @namespace
*/
cssProps: {
/**
* Disables text selection to improve the dragging gesture. Mainly for desktop browsers.
* @type {String}
* @default 'none'
*/
userSelect: 'none',
/**
* Disable the Windows Phone grippers when pressing an element.
* @type {String}
* @default 'none'
*/
touchSelect: 'none',
/**
* Disables the default callout shown when you touch and hold a touch target.
* On iOS, when you touch and hold a touch target such as a link, Safari displays
* a callout containing information about the link. This property allows you to disable that callout.
* @type {String}
* @default 'none'
*/
touchCallout: 'none',
/**
* Specifies whether zooming is enabled. Used by IE10>
* @type {String}
* @default 'none'
*/
contentZooming: 'none',
/**
* Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers.
* @type {String}
* @default 'none'
*/
userDrag: 'none',
/**
* Overrides the highlight color shown when the user taps a link or a JavaScript
* clickable element in iOS. This property obeys the alpha value, if specified.
* @type {String}
* @default 'rgba(0,0,0,0)'
*/
tapHighlightColor: 'rgba(0,0,0,0)'
}
};
var STOP = 1;
var FORCED_STOP = 2;
/**
* Manager
* @param {HTMLElement} element
* @param {Object} [options]
* @constructor
*/
function Manager(element, options) {
options = options || {};
this.options = merge(options, Hammer.defaults);
this.options.inputTarget = this.options.inputTarget || element;
this.handlers = {};
this.session = {};
this.recognizers = [];
this.element = element;
this.input = createInputInstance(this);
this.touchAction = new TouchAction(this, this.options.touchAction);
toggleCssProps(this, true);
each(options.recognizers, function(item) {
var recognizer = this.add(new (item[0])(item[1]));
item[2] && recognizer.recognizeWith(item[2]);
item[3] && recognizer.requireFailure(item[3]);
}, this);
}
Manager.prototype = {
/**
* set options
* @param {Object} options
* @returns {Manager}
*/
set: function(options) {
extend(this.options, options);
// Options that need a little more setup
if (options.touchAction) {
this.touchAction.update();
}
if (options.inputTarget) {
// Clean up existing event listeners and reinitialize
this.input.destroy();
this.input.target = options.inputTarget;
this.input.init();
}
return this;
},
/**
* stop recognizing for this session.
* This session will be discarded, when a new [input]start event is fired.
* When forced, the recognizer cycle is stopped immediately.
* @param {Boolean} [force]
*/
stop: function(force) {
this.session.stopped = force ? FORCED_STOP : STOP;
},
/**
* run the recognizers!
* called by the inputHandler function on every movement of the pointers (touches)
* it walks through all the recognizers and tries to detect the gesture that is being made
* @param {Object} inputData
*/
recognize: function(inputData) {
var session = this.session;
if (session.stopped) {
return;
}
// run the touch-action polyfill
this.touchAction.preventDefaults(inputData);
var recognizer;
var recognizers = this.recognizers;
// this holds the recognizer that is being recognized.
// so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED
// if no recognizer is detecting a thing, it is set to `null`
var curRecognizer = session.curRecognizer;
// reset when the last recognizer is recognized
// or when we're in a new session
if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) {
curRecognizer = session.curRecognizer = null;
}
var i = 0;
while (i < recognizers.length) {
recognizer = recognizers[i];
// find out if we are allowed try to recognize the input for this one.
// 1. allow if the session is NOT forced stopped (see the .stop() method)
// 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one
// that is being recognized.
// 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer.
// this can be setup with the `recognizeWith()` method on the recognizer.
if (session.stopped !== FORCED_STOP && ( // 1
!curRecognizer || recognizer == curRecognizer || // 2
recognizer.canRecognizeWith(curRecognizer))) { // 3
recognizer.recognize(inputData);
} else {
recognizer.reset();
}
// if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the
// current active recognizer. but only if we don't already have an active recognizer
if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) {
curRecognizer = session.curRecognizer = recognizer;
}
i++;
}
},
/**
* get a recognizer by its event name.
* @param {Recognizer|String} recognizer
* @returns {Recognizer|Null}
*/
get: function(recognizer) {
if (recognizer instanceof Recognizer) {
return recognizer;
}
var recognizers = this.recognizers;
for (var i = 0; i < recognizers.length; i++) {
if (recognizers[i].options.event == recognizer) {
return recognizers[i];
}
}
return null;
},
/**
* add a recognizer to the manager
* existing recognizers with the same event name will be removed
* @param {Recognizer} recognizer
* @returns {Recognizer|Manager}
*/
add: function(recognizer) {
if (invokeArrayArg(recognizer, 'add', this)) {
return this;
}
// remove existing
var existing = this.get(recognizer.options.event);
if (existing) {
this.remove(existing);
}
this.recognizers.push(recognizer);
recognizer.manager = this;
this.touchAction.update();
return recognizer;
},
/**
* remove a recognizer by name or instance
* @param {Recognizer|String} recognizer
* @returns {Manager}
*/
remove: function(recognizer) {
if (invokeArrayArg(recognizer, 'remove', this)) {
return this;
}
var recognizers = this.recognizers;
recognizer = this.get(recognizer);
recognizers.splice(inArray(recognizers, recognizer), 1);
this.touchAction.update();
return this;
},
/**
* bind event
* @param {String} events
* @param {Function} handler
* @returns {EventEmitter} this
*/
on: function(events, handler) {
var handlers = this.handlers;
each(splitStr(events), function(event) {
handlers[event] = handlers[event] || [];
handlers[event].push(handler);
});
return this;
},
/**
* unbind event, leave emit blank to remove all handlers
* @param {String} events
* @param {Function} [handler]
* @returns {EventEmitter} this
*/
off: function(events, handler) {
var handlers = this.handlers;
each(splitStr(events), function(event) {
if (!handler) {
delete handlers[event];
} else {
handlers[event].splice(inArray(handlers[event], handler), 1);
}
});
return this;
},
/**
* emit event to the listeners
* @param {String} event
* @param {Object} data
*/
emit: function(event, data) {
// we also want to trigger dom events
if (this.options.domEvents) {
triggerDomEvent(event, data);
}
// no handlers, so skip it all
var handlers = this.handlers[event] && this.handlers[event].slice();
if (!handlers || !handlers.length) {
return;
}
data.type = event;
data.preventDefault = function() {
data.srcEvent.preventDefault();
};
var i = 0;
while (i < handlers.length) {
handlers[i](data);
i++;
}
},
/**
* destroy the manager and unbinds all events
* it doesn't unbind dom events, that is the user own responsibility
*/
destroy: function() {
this.element && toggleCssProps(this, false);
this.handlers = {};
this.session = {};
this.input.destroy();
this.element = null;
}
};
/**
* add/remove the css properties as defined in manager.options.cssProps
* @param {Manager} manager
* @param {Boolean} add
*/
function toggleCssProps(manager, add) {
var element = manager.element;
each(manager.options.cssProps, function(value, name) {
element.style[prefixed(element.style, name)] = add ? value : '';
});
}
/**
* trigger dom event
* @param {String} event
* @param {Object} data
*/
function triggerDomEvent(event, data) {
var gestureEvent = DOCUMENT.createEvent('Event');
gestureEvent.initEvent(event, true, true);
gestureEvent.gesture = data;
data.target.dispatchEvent(gestureEvent);
}
extend(Hammer, {
INPUT_START: INPUT_START,
INPUT_MOVE: INPUT_MOVE,
INPUT_END: INPUT_END,
INPUT_CANCEL: INPUT_CANCEL,
STATE_POSSIBLE: STATE_POSSIBLE,
STATE_BEGAN: STATE_BEGAN,
STATE_CHANGED: STATE_CHANGED,
STATE_ENDED: STATE_ENDED,
STATE_RECOGNIZED: STATE_RECOGNIZED,
STATE_CANCELLED: STATE_CANCELLED,
STATE_FAILED: STATE_FAILED,
DIRECTION_NONE: DIRECTION_NONE,
DIRECTION_LEFT: DIRECTION_LEFT,
DIRECTION_RIGHT: DIRECTION_RIGHT,
DIRECTION_UP: DIRECTION_UP,
DIRECTION_DOWN: DIRECTION_DOWN,
DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL,
DIRECTION_VERTICAL: DIRECTION_VERTICAL,
DIRECTION_ALL: DIRECTION_ALL,
Manager: Manager,
Input: Input,
TouchAction: TouchAction,
TouchInput: TouchInput,
MouseInput: MouseInput,
PointerEventInput: PointerEventInput,
TouchMouseInput: TouchMouseInput,
Recognizer: Recognizer,
AttrRecognizer: AttrRecognizer,
Tap: TapRecognizer,
Pan: PanRecognizer,
Swipe: SwipeRecognizer,
Pinch: PinchRecognizer,
Rotate: RotateRecognizer,
Press: PressRecognizer,
on: addEventListeners,
off: removeEventListeners,
each: each,
merge: merge,
extend: extend,
inherit: inherit,
bindFn: bindFn,
prefixed: prefixed
});
return Hammer;
};
},{"utils":731}],636:[function(require,module,exports){
module.exports=require(267)
},{"event":640,"js-ext/extra/hashmap.js":656,"js-ext/lib/array.js":657,"js-ext/lib/object.js":658,"js-ext/lib/string.js":659,"polyfill/polyfill-base.js":671,"utils":672,"vdom":730}],637:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":642,"js-ext/lib/object.js":643,"polyfill/polyfill-base.js":655}],638:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":637}],639:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":637,"js-ext/extra/classes.js":641,"js-ext/lib/object.js":643}],640:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":637,"./event-emitter.js":638,"./event-listener.js":639}],641:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":643,"js-ext/extra/hashmap.js":642,"polyfill/polyfill-base.js":646}],642:[function(require,module,exports){
module.exports=require(4)
},{}],643:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":642,"polyfill/polyfill-base.js":646,"utils":647}],644:[function(require,module,exports){
module.exports=require(14)
},{}],645:[function(require,module,exports){
module.exports=require(15)
},{}],646:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":644,"./lib/window.console.js":645}],647:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":648,"./lib/timers.js":649}],648:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":642,"polyfill/polyfill-base.js":652}],649:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":652}],650:[function(require,module,exports){
module.exports=require(14)
},{}],651:[function(require,module,exports){
module.exports=require(15)
},{}],652:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":650,"./lib/window.console.js":651}],653:[function(require,module,exports){
module.exports=require(14)
},{}],654:[function(require,module,exports){
module.exports=require(15)
},{}],655:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":653,"./lib/window.console.js":654}],656:[function(require,module,exports){
module.exports=require(4)
},{}],657:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":662}],658:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":656,"polyfill/polyfill-base.js":662,"utils":663}],659:[function(require,module,exports){
module.exports=require(29)
},{}],660:[function(require,module,exports){
module.exports=require(14)
},{}],661:[function(require,module,exports){
module.exports=require(15)
},{}],662:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":660,"./lib/window.console.js":661}],663:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":664,"./lib/timers.js":665}],664:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":656,"polyfill/polyfill-base.js":668}],665:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":668}],666:[function(require,module,exports){
module.exports=require(14)
},{}],667:[function(require,module,exports){
module.exports=require(15)
},{}],668:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":666,"./lib/window.console.js":667}],669:[function(require,module,exports){
module.exports=require(14)
},{}],670:[function(require,module,exports){
module.exports=require(15)
},{}],671:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":669,"./lib/window.console.js":670}],672:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":673,"./lib/timers.js":674}],673:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":656,"polyfill/polyfill-base.js":677}],674:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":677}],675:[function(require,module,exports){
module.exports=require(14)
},{}],676:[function(require,module,exports){
module.exports=require(15)
},{}],677:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":675,"./lib/window.console.js":676}],678:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],679:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":683,"js-ext/extra/hashmap.js":680,"polyfill/polyfill-base.js":689}],680:[function(require,module,exports){
module.exports=require(4)
},{}],681:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":682,"../lib/object.js":683,"./classes.js":679,"js-ext/extra/hashmap.js":680,"polyfill/lib/weakmap.js":687}],682:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":689}],683:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":680,"polyfill/polyfill-base.js":689,"utils":690}],684:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":689}],685:[function(require,module,exports){
module.exports=require(29)
},{}],686:[function(require,module,exports){
module.exports=require(14)
},{}],687:[function(require,module,exports){
module.exports=require(57)
},{}],688:[function(require,module,exports){
module.exports=require(15)
},{}],689:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":686,"./lib/window.console.js":688}],690:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":691,"./lib/timers.js":692}],691:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":680,"polyfill/polyfill-base.js":695}],692:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":695}],693:[function(require,module,exports){
module.exports=require(14)
},{}],694:[function(require,module,exports){
module.exports=require(15)
},{}],695:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":693,"./lib/window.console.js":694}],696:[function(require,module,exports){
module.exports=require(66)
},{}],697:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":696}],698:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":696}],699:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":696}],700:[function(require,module,exports){
module.exports=require(14)
},{}],701:[function(require,module,exports){
module.exports=require(15)
},{}],702:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":700,"./lib/window.console.js":701}],703:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":704,"./lib/timers.js":705}],704:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":680,"polyfill/polyfill-base.js":708}],705:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":708}],706:[function(require,module,exports){
module.exports=require(14)
},{}],707:[function(require,module,exports){
module.exports=require(15)
},{}],708:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":706,"./lib/window.console.js":707}],709:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":710}],710:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":711,"js-ext/lib/object.js":712}],711:[function(require,module,exports){
module.exports=require(4)
},{}],712:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":711,"polyfill/polyfill-base.js":715,"utils":716}],713:[function(require,module,exports){
module.exports=require(14)
},{}],714:[function(require,module,exports){
module.exports=require(15)
},{}],715:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":713,"./lib/window.console.js":714}],716:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":717,"./lib/timers.js":718}],717:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":711,"polyfill/polyfill-base.js":721}],718:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":721}],719:[function(require,module,exports){
module.exports=require(14)
},{}],720:[function(require,module,exports){
module.exports=require(15)
},{}],721:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":719,"./lib/window.console.js":720}],722:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":680,"js-ext/lib/object.js":683,"js-ext/lib/string.js":685,"polyfill":702,"polyfill/extra/transition.js":697,"polyfill/extra/vendorCSS.js":699}],723:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":680,"js-ext/lib/object.js":683,"polyfill":702}],724:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":680,"js-ext/lib/object.js":683,"js-ext/lib/string.js":685,"polyfill":702}],725:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":678,"./attribute-extractor.js":722,"./element-array.js":723,"./html-parser.js":726,"./node-parser.js":727,"./vdom-ns.js":728,"./vnode.js":729,"js-ext/extra/hashmap.js":680,"js-ext/lib/object.js":683,"js-ext/lib/promise.js":684,"js-ext/lib/string.js":685,"polyfill":702,"polyfill/extra/transition.js":697,"polyfill/extra/transitionend.js":698,"polyfill/extra/vendorCSS.js":699,"utils":703,"window-ext":709}],726:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":722,"./vdom-ns.js":728,"js-ext/extra/hashmap.js":680,"js-ext/lib/object.js":683,"polyfill":702}],727:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":722,"./vdom-ns.js":728,"./vnode.js":729,"js-ext/extra/hashmap.js":680,"js-ext/lib/object.js":683,"polyfill":702}],728:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":680,"js-ext/lib/object.js":683,"polyfill":702}],729:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":722,"./html-parser.js":726,"./vdom-ns.js":728,"js-ext/extra/hashmap.js":680,"js-ext/extra/lightmap.js":681,"js-ext/lib/array.js":682,"js-ext/lib/object.js":683,"js-ext/lib/string.js":685,"polyfill":702,"utils/lib/timers.js":705}],730:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":724,"./partials/extend-element.js":725,"./partials/node-parser.js":727,"js-ext/extra/hashmap.js":680,"js-ext/lib/object.js":683,"utils/lib/timers.js":705}],731:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":732,"./lib/timers.js":733}],732:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1046,"polyfill/polyfill-base.js":736}],733:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":736}],734:[function(require,module,exports){
module.exports=require(14)
},{}],735:[function(require,module,exports){
module.exports=require(15)
},{}],736:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":734,"./lib/window.console.js":735}],737:[function(require,module,exports){
var css = "[plugin-fm=\"true\"] {\n /* NEVER can we select the text: when the focusmanager is active it will refocus on the active item */\n -moz-user-select: none;\n -khtml-user-select: none;\n -webkit-user-select: none;\n -ms-user-select: none;\n user-select: none;\n cursor: default;\n}"; (require("/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify"))(css); module.exports = css;
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],738:[function(require,module,exports){
"use strict";
require('js-ext/lib/object.js');
require('polyfill');
require('./css/focusmanager.css');
/**
*
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module focusmanager
* @class FocusManager
* @since 0.0.1
*/
var NAME = '[focusmanager]: ',
async = require('utils').async,
createHashMap = require('js-ext/extra/hashmap.js').createMap,
DEFAULT_SELECTOR = 'input, button, select, textarea, .focusable, [plugin-fm="true"], [itag-formelement="true"]',
// SPECIAL_KEYS needs to be a native Object --> we need .some()
SPECIAL_KEYS = {
shift: 'shiftKey',
ctrl: 'ctrlKey',
cmd: 'metaKey',
alt: 'altKey'
},
DEFAULT_KEYUP = 'shift+9',
DEFAULT_KEYDOWN = '9',
DEFAULT_NOLOOP = false,
FM_SELECTION = 'fm-selection',
FM_SELECTION_START = FM_SELECTION+'start',
FM_SELECTION_END = FM_SELECTION+'end',
FOCUSSED = 'focussed';
module.exports = function (window) {
var DOCUMENT = window.document,
FocusManager, Event, nextFocusNode, searchFocusNode, markAsFocussed,
resetLastValue, getFocusManagerSelector, setupEvents, defineFocusEvent;
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
/*jshint boss:true */
if (FocusManager=window._ITSAmodules.FocusManager) {
/*jshint boss:false */
return FocusManager; // FocusManager was already created
}
require('window-ext')(window);
require('node-plugin')(window);
Event = require('event-mobile')(window);
getFocusManagerSelector = function(focusContainerNode) {
var selector = focusContainerNode._plugin.fm.model.manage;
(selector.toLowerCase()==='true') && (selector=DEFAULT_SELECTOR);
return selector;
};
nextFocusNode = function(e, keyCode, actionkey, focusContainerNode, sourceNode, selector, downwards, initialSourceNode) {
console.log(NAME+'nextFocusNode');
var keys, lastIndex, i, specialKeysMatch, specialKey, len, enterPressedOnInput, primaryButtons,
inputType, foundNode, formNode, primaryonenter, noloop, nodeHit, foundContainer;
keys = actionkey.split('+');
len = keys.length;
lastIndex = len - 1;
if ((keyCode===13) && (sourceNode.getTagName()==='INPUT')) {
inputType = sourceNode.getAttr('type').toLowerCase();
enterPressedOnInput = (inputType==='text') || (inputType==='password');
}
if (enterPressedOnInput) {
// check if we need to press the primary button - if available
/*jshint boss:true */
if ((primaryonenter=sourceNode.getAttr('fm-primaryonenter')) && (primaryonenter.toLowerCase()==='true')) {
/*jshint boss:false */
primaryButtons = focusContainerNode.getAll('button.pure-button-primary');
primaryButtons.some(function(buttonNode) {
buttonNode.matches(selector) && (foundNode=buttonNode);
return foundNode;
});
if (foundNode) {
async(function() {
Event.emit(foundNode, 'UI:tap');
// _buttonPressed make event-dom to simulate a pressed button for 200ms
Event.emit(foundNode, 'UI:tap', {_buttonPressed: true});
// if the button is of type `submit`, then try to submit the form
formNode = foundNode.inside('form');
formNode && formNode.submit();
});
return foundNode;
}
}
}
// double == --> keyCode is number, keys is a string
if (enterPressedOnInput || (keyCode==keys[lastIndex])) {
// posible keyup --> check if special characters match:
specialKeysMatch = true;
SPECIAL_KEYS.some(function(value) {
specialKeysMatch = !e[value];
return !specialKeysMatch;
});
for (i=lastIndex-1; (i>=0) && !specialKeysMatch; i--) {
specialKey = keys[i].toLowerCase();
specialKeysMatch = e[SPECIAL_KEYS[specialKey]];
}
}
if (specialKeysMatch) {
noloop = focusContainerNode._plugin.fm.model.noloop;
// in case sourceNode is an innernode of a selector, we need to start from the selector:
sourceNode.matches(selector) || (sourceNode=sourceNode.inside(selector));
if (downwards) {
nodeHit = sourceNode;
/*jshint noempty:true */
while ((nodeHit=nodeHit.next(selector, focusContainerNode)) && (nodeHit.getStyle('display')==='none')) {}
/*jshint noempty:false */
if (!nodeHit) {
nodeHit = noloop ? sourceNode.last(selector, focusContainerNode) : sourceNode.first(selector, focusContainerNode);
if (nodeHit.getStyle('display')==='none') {
/*jshint noempty:true */
while ((nodeHit=nodeHit[noloop ? 'previous' : 'next'](selector, focusContainerNode)) && (nodeHit.getStyle('display')==='none')) {}
/*jshint noempty:false */
}
}
}
else {
nodeHit = sourceNode;
/*jshint noempty:true */
while ((nodeHit=nodeHit.previous(selector, focusContainerNode)) && (nodeHit.getStyle('display')==='none')) {}
/*jshint noempty:false */
if (!nodeHit) {
nodeHit = noloop ? sourceNode.first(selector, focusContainerNode) : sourceNode.last(selector, focusContainerNode);
if (nodeHit.getStyle('display')==='none') {
/*jshint noempty:true */
while ((nodeHit=nodeHit[noloop ? 'next' : 'previous'](selector, focusContainerNode)) && (nodeHit.getStyle('display')==='none')) {}
/*jshint noempty:false */
}
}
}
if (nodeHit===sourceNode) {
// cannot found another, return itself, BUT return `initialSourceNode` if it is available
return initialSourceNode || sourceNode;
}
else {
foundContainer = nodeHit.inside('[plugin-fm="true"]');
// only if `nodeHit` is inside the runniong focusContainer, we may return it,
// otherwise look further
return (foundContainer===focusContainerNode) ? nodeHit : nextFocusNode(e, keyCode, actionkey, focusContainerNode, nodeHit, selector, downwards, sourceNode);
}
}
return false;
};
markAsFocussed = function(focusContainerNode, node) {
console.log(NAME+'markAsFocussed');
var selector = getFocusManagerSelector(focusContainerNode),
index = focusContainerNode.getAll(selector).indexOf(node) || 0;
// we also need to set the appropriate nodeData, so that when the itags re-render,
// they don't reset this particular information
resetLastValue(focusContainerNode);
// also store the lastitem's index --> in case the node gets removed,
// or re-rendering itags which don't have the attribute-data.
// otherwise, a refocus on the container will set the focus to the nearest item
focusContainerNode.setData('fm-lastitem-bkp', index);
node.setData('fm-tabindex', true);
node.setAttrs([
{name: 'tabindex', value: '0'},
{name: 'fm-lastitem', value: true}
], true);
};
resetLastValue = function(focusContainerNode) {
var lastItemNodes = focusContainerNode.getAll('[fm-lastitem]');
lastItemNodes.removeAttrs(['fm-lastitem', 'tabindex'], true)
.removeData('fm-tabindex');
focusContainerNode.removeData('fm-lastitem-bkp');
};
searchFocusNode = function(initialNode, deeper) {
console.log(NAME+'searchFocusNode');
var focusContainerNode = initialNode.hasAttr('fm-manage') ? initialNode : initialNode.inside('[plugin-fm="true"]'),
focusNode, alwaysDefault, selector, allFocusableNodes, index, parentContainerNode, parentSelector;
if (focusContainerNode) {
selector = getFocusManagerSelector(focusContainerNode);
focusNode = initialNode.matches(selector) ? initialNode : initialNode.inside(selector);
// focusNode can only be equal focusContainerNode when focusContainerNode lies with a focusnode itself with that particular selector:
if (focusNode===focusContainerNode) {
parentContainerNode = focusNode.inside('[plugin-fm="true"]');
if (parentContainerNode) {
parentSelector = getFocusManagerSelector(parentContainerNode);
if (!focusNode.matches(parentSelector) || deeper) {
focusNode = null;
}
}
else {
focusNode = null;
}
}
if (focusNode && focusContainerNode.contains(focusNode, true)) {
markAsFocussed(parentContainerNode || focusContainerNode, focusNode);
}
else {
// find the right node that should get focus
/*jshint boss:true */
alwaysDefault = focusContainerNode._plugin.fm.model.alwaysdefault;
/*jshint boss:false */
alwaysDefault && (focusNode=focusContainerNode.getElement('[fm-defaultitem="true"]'));
if (!focusNode) {
// search for last item
focusNode = focusContainerNode.getElement('[fm-lastitem="true"]');
if (!focusNode) {
// look at the lastitemindex of the focuscontainer
index = focusContainerNode.getData('fm-lastitem-bkp');
if (index!==undefined) {
allFocusableNodes = focusContainerNode.getAll(selector);
focusNode = allFocusableNodes[index];
}
}
}
// still not found and alwaysDefault was falsy: try the defualt node:
!focusNode && !alwaysDefault && (focusNode=focusContainerNode.getElement('[fm-defaultitem="true"]'));
// still not found: try the first focussable node (which we might find inside `allFocusableNodes`:
!focusNode && (focusNode = allFocusableNodes ? allFocusableNodes[0] : focusContainerNode.getElement(selector));
if (focusNode) {
markAsFocussed(parentContainerNode || focusContainerNode, focusNode);
}
else {
focusNode = initialNode;
}
}
}
else {
focusNode = initialNode;
}
return focusNode;
};
setupEvents = function() {
Event.before('keydown', function(e) {
console.log(NAME+'before keydown-event');
var focusContainerNode,
sourceNode = e.target,
selector, keyCode, actionkey, focusNode, keys, len, lastIndex, specialKeysMatch, i, specialKey;
focusContainerNode = sourceNode.inside('[plugin-fm="true"]');
if (focusContainerNode) {
// key was pressed inside a focusmanagable container
selector = getFocusManagerSelector(focusContainerNode);
keyCode = e.keyCode;
// first check for keydown:
actionkey = focusContainerNode._plugin.fm.model.keydown;
focusNode = nextFocusNode(e, keyCode, actionkey, focusContainerNode, sourceNode, selector, true);
if (!focusNode) {
// check for keyup:
actionkey = focusContainerNode._plugin.fm.model.keyup;
focusNode = nextFocusNode(e, keyCode, actionkey, focusContainerNode, sourceNode, selector);
}
if (!focusNode) {
// check for keyenter, but only when e.target equals a focusmanager:
if (sourceNode.matches('[plugin-fm="true"]')) {
actionkey = sourceNode._plugin.fm.model.keyenter;
if (actionkey) {
keys = actionkey.split('+');
len = keys.length;
lastIndex = len - 1;
// double == --> keyCode is number, keys is a string
if (keyCode==keys[lastIndex]) {
// posible keyup --> check if special characters match:
specialKeysMatch = true;
SPECIAL_KEYS.some(function(value) {
specialKeysMatch = !e[value];
return !specialKeysMatch;
});
for (i=lastIndex-1; (i>=0) && !specialKeysMatch; i--) {
specialKey = keys[i].toLowerCase();
specialKeysMatch = e[SPECIAL_KEYS[specialKey]];
}
}
if (specialKeysMatch) {
resetLastValue(sourceNode);
focusNode = searchFocusNode(sourceNode, true);
}
}
}
}
if (!focusNode) {
// check for keyleave:
actionkey = focusContainerNode._plugin.fm.model.keyleave;
if (actionkey) {
keys = actionkey.split('+');
len = keys.length;
lastIndex = len - 1;
// double == --> keyCode is number, keys is a string
if (keyCode==keys[lastIndex]) {
// posible keyup --> check if special characters match:
specialKeysMatch = true;
SPECIAL_KEYS.some(function(value) {
specialKeysMatch = !e[value];
return !specialKeysMatch;
});
for (i=lastIndex-1; (i>=0) && !specialKeysMatch; i--) {
specialKey = keys[i].toLowerCase();
specialKeysMatch = e[SPECIAL_KEYS[specialKey]];
}
}
if (specialKeysMatch) {
resetLastValue(focusContainerNode);
focusNode = focusContainerNode;
}
}
}
if (focusNode) {
e.preventDefaultContinue();
// prevent default action --> we just want to re-focus, but we DO want afterlisteners
// to be handled in the after-listener: someone else might want to halt the keydown event.
e._focusNode = focusNode;
}
}
});
Event.after('keydown', function(e) {
console.log(NAME+'after keydown-event');
var focusNode = e._focusNode;
if (focusNode && focusNode.focus) {
focusNode.focus();
}
});
Event.after('focus', function(e) {
console.log(NAME+'after focus-event');
var node = e.target,
body = DOCUMENT.body,
cleanFocussedData = function(element, loop) {
if (element.removeData) {
do {
// we also need to set the appropriate nodeData, so that when the itags re-render,
// they don't reset this particular information
element.removeData(FOCUSSED);
element.removeClass(FOCUSSED, null, null, true);
element = (element===body) ? null : element.getParent();
} while (element && loop);
}
};
// first, unfocus currently focussed items and up the tree
DOCUMENT.getAll('.'+FOCUSSED, true).forEach(cleanFocussedData);
if (node && node.setClass) {
do {
// we also need to set the appropriate nodeData, so that when the itags re-render,
// they don't reset this particular information
node.setData(FOCUSSED, true);
node.setClass(FOCUSSED, null, null, true);
node = (node===body) ? null : node.getParent();
} while (node);
}
}, true); // set in front: we need to make use of the previous DOCUMENT._activeElement, before it gets updated by event-dom
// focus-fix for keeping focus when a mouse gets down for a longer time
Event.after('mousedown', function(e) {
console.log(NAME+'after focus-event');
var node = e.target;
if (!node.hasFocus()) {
node.focus();
}
}, 'button');
Event.after('tap', function(e) {
console.log(NAME+'after tap-event');
var focusNode = e.target,
focusContainerNode;
if (e._noFocus) {
return;
}
if (focusNode && focusNode.inside) {
focusContainerNode = focusNode.hasAttr('plugin-fm') ? focusNode : focusNode.inside('[plugin-fm="true"]');
}
if (focusContainerNode) {
if ((focusNode===focusContainerNode) || !focusNode.matches(getFocusManagerSelector(focusContainerNode))) {
focusNode = searchFocusNode(focusNode, true);
}
if (focusNode.hasFocus()) {
markAsFocussed(focusContainerNode, focusNode);
}
else {
focusNode.focus();
}
}
}, null, null, true);
Event.after(['keypress', 'mouseup', 'panup', 'mousedown', 'pandown'], function(e) {
console.log(NAME+'after '+e.type+'-event');
var focusContainerNode,
sourceNode = e.target,
selector;
focusContainerNode = sourceNode.inside('[plugin-fm="true"]');
if (focusContainerNode) {
// key was pressed inside a focusmanagable container
selector = getFocusManagerSelector(focusContainerNode);
if (sourceNode.matches(selector)) {
sourceNode.setAttr(FM_SELECTION_START, sourceNode.selectionStart || '0', true)
.setAttr(FM_SELECTION_END, sourceNode.selectionEnd || '0', true);
}
}
}, 'input[type="text"], textarea');
Event.after('focus', function(e) {
console.log(NAME+'after focus-event');
var focusContainerNode,
sourceNode = e.target,
selector, selectionStart, selectionEnd;
focusContainerNode = sourceNode.inside('[plugin-fm="true"]');
if (focusContainerNode) {
// key was pressed inside a focusmanagable container
selector = getFocusManagerSelector(focusContainerNode);
if (sourceNode.matches(selector)) {
// cautious: fm-selectionstart can be 0 --> which would lead into a falsy value
selectionStart = sourceNode.getAttr(FM_SELECTION_START);
(selectionStart===undefined) && (selectionStart=sourceNode.getValue().length);
selectionEnd = Math.max(sourceNode.getAttr(FM_SELECTION_END) || selectionStart, selectionStart);
sourceNode.selectionEnd = selectionEnd;
sourceNode.selectionStart = selectionStart;
markAsFocussed(focusContainerNode, sourceNode);
}
}
}, 'input[type="text"], textarea');
};
setupEvents();
window._ITSAmodules.FocusManager = FocusManager = DOCUMENT.definePlugin('fm', null, {
attrs: {
manage: 'string',
alwaysdefault: 'boolean',
keyup: 'string',
keydown: 'string',
keyenter: 'string',
keyleave: 'string',
noloop: 'boolean'
},
defaults: {
manage: 'true',
alwaysdefault: false,
keyup: DEFAULT_KEYUP,
keydown: DEFAULT_KEYDOWN,
noloop: DEFAULT_NOLOOP
}
});
defineFocusEvent = function(customevent) {
Event.defineEvent(customevent)
.defaultFn(function(e) {
var node = e.target,
leftScroll = window.getScrollLeft(),
topScroll = window.getScrollTop();
node._focus();
// reset winscroll:
window.scrollTo(leftScroll, topScroll);
// make sure the node is inside the viewport:
// node.forceIntoView();
});
};
(function(HTMLElementPrototype) {
HTMLElementPrototype._focus = HTMLElementPrototype.focus;
HTMLElementPrototype.focus = function(noRefocus) {
console.log(NAME+'focus');
/**
* In case of a manual focus (node.focus()) the node will fire an `manualfocus`-event
* which can be prevented.
* @event manualfocus
*/
var focusElement = this,
doEmit, focusContainerNode;
doEmit = function(focusNode) {
var emitterName = focusNode._emitterName,
customevent = emitterName+':manualfocus';
Event._ce[customevent] || defineFocusEvent(customevent);
focusNode.emit('manualfocus');
};
if (noRefocus) {
doEmit(focusElement);
}
else {
focusContainerNode = (this.getAttr('plugin-fm')==='true') ? focusElement : focusElement.inside('[plugin-fm="true"]');
if (focusContainerNode) {
focusContainerNode.pluginReady('fm').then(
function() {
doEmit(searchFocusNode(focusElement));
}
);
}
else {
doEmit(focusElement);
}
}
};
}(window.HTMLElement.prototype));
return FocusManager;
};
},{"./css/focusmanager.css":737,"event-mobile":739,"js-ext/extra/hashmap.js":842,"js-ext/lib/object.js":843,"node-plugin":853,"polyfill":1025,"utils":1026,"window-ext":1032}],739:[function(require,module,exports){
module.exports=require(634)
},{"./lib/hammer-2.0.4.js":740,"event-dom":741}],740:[function(require,module,exports){
module.exports=require(635)
},{"utils":836}],741:[function(require,module,exports){
module.exports=require(267)
},{"event":745,"js-ext/extra/hashmap.js":761,"js-ext/lib/array.js":762,"js-ext/lib/object.js":763,"js-ext/lib/string.js":764,"polyfill/polyfill-base.js":776,"utils":777,"vdom":835}],742:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":747,"js-ext/lib/object.js":748,"polyfill/polyfill-base.js":760}],743:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":742}],744:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":742,"js-ext/extra/classes.js":746,"js-ext/lib/object.js":748}],745:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":742,"./event-emitter.js":743,"./event-listener.js":744}],746:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":748,"js-ext/extra/hashmap.js":747,"polyfill/polyfill-base.js":751}],747:[function(require,module,exports){
module.exports=require(4)
},{}],748:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":747,"polyfill/polyfill-base.js":751,"utils":752}],749:[function(require,module,exports){
module.exports=require(14)
},{}],750:[function(require,module,exports){
module.exports=require(15)
},{}],751:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":749,"./lib/window.console.js":750}],752:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":753,"./lib/timers.js":754}],753:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":747,"polyfill/polyfill-base.js":757}],754:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":757}],755:[function(require,module,exports){
module.exports=require(14)
},{}],756:[function(require,module,exports){
module.exports=require(15)
},{}],757:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":755,"./lib/window.console.js":756}],758:[function(require,module,exports){
module.exports=require(14)
},{}],759:[function(require,module,exports){
module.exports=require(15)
},{}],760:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":758,"./lib/window.console.js":759}],761:[function(require,module,exports){
module.exports=require(4)
},{}],762:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":767}],763:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":761,"polyfill/polyfill-base.js":767,"utils":768}],764:[function(require,module,exports){
module.exports=require(29)
},{}],765:[function(require,module,exports){
module.exports=require(14)
},{}],766:[function(require,module,exports){
module.exports=require(15)
},{}],767:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":765,"./lib/window.console.js":766}],768:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":769,"./lib/timers.js":770}],769:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":761,"polyfill/polyfill-base.js":773}],770:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":773}],771:[function(require,module,exports){
module.exports=require(14)
},{}],772:[function(require,module,exports){
module.exports=require(15)
},{}],773:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":771,"./lib/window.console.js":772}],774:[function(require,module,exports){
module.exports=require(14)
},{}],775:[function(require,module,exports){
module.exports=require(15)
},{}],776:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":774,"./lib/window.console.js":775}],777:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":778,"./lib/timers.js":779}],778:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":761,"polyfill/polyfill-base.js":782}],779:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":782}],780:[function(require,module,exports){
module.exports=require(14)
},{}],781:[function(require,module,exports){
module.exports=require(15)
},{}],782:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":780,"./lib/window.console.js":781}],783:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],784:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":788,"js-ext/extra/hashmap.js":785,"polyfill/polyfill-base.js":794}],785:[function(require,module,exports){
module.exports=require(4)
},{}],786:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":787,"../lib/object.js":788,"./classes.js":784,"js-ext/extra/hashmap.js":785,"polyfill/lib/weakmap.js":792}],787:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":794}],788:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":785,"polyfill/polyfill-base.js":794,"utils":795}],789:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":794}],790:[function(require,module,exports){
module.exports=require(29)
},{}],791:[function(require,module,exports){
module.exports=require(14)
},{}],792:[function(require,module,exports){
module.exports=require(57)
},{}],793:[function(require,module,exports){
module.exports=require(15)
},{}],794:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":791,"./lib/window.console.js":793}],795:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":796,"./lib/timers.js":797}],796:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":785,"polyfill/polyfill-base.js":800}],797:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":800}],798:[function(require,module,exports){
module.exports=require(14)
},{}],799:[function(require,module,exports){
module.exports=require(15)
},{}],800:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":798,"./lib/window.console.js":799}],801:[function(require,module,exports){
module.exports=require(66)
},{}],802:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":801}],803:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":801}],804:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":801}],805:[function(require,module,exports){
module.exports=require(14)
},{}],806:[function(require,module,exports){
module.exports=require(15)
},{}],807:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":805,"./lib/window.console.js":806}],808:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":809,"./lib/timers.js":810}],809:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":785,"polyfill/polyfill-base.js":813}],810:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":813}],811:[function(require,module,exports){
module.exports=require(14)
},{}],812:[function(require,module,exports){
module.exports=require(15)
},{}],813:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":811,"./lib/window.console.js":812}],814:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":815}],815:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":816,"js-ext/lib/object.js":817}],816:[function(require,module,exports){
module.exports=require(4)
},{}],817:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":816,"polyfill/polyfill-base.js":820,"utils":821}],818:[function(require,module,exports){
module.exports=require(14)
},{}],819:[function(require,module,exports){
module.exports=require(15)
},{}],820:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":818,"./lib/window.console.js":819}],821:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":822,"./lib/timers.js":823}],822:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":816,"polyfill/polyfill-base.js":826}],823:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":826}],824:[function(require,module,exports){
module.exports=require(14)
},{}],825:[function(require,module,exports){
module.exports=require(15)
},{}],826:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":824,"./lib/window.console.js":825}],827:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":785,"js-ext/lib/object.js":788,"js-ext/lib/string.js":790,"polyfill":807,"polyfill/extra/transition.js":802,"polyfill/extra/vendorCSS.js":804}],828:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":785,"js-ext/lib/object.js":788,"polyfill":807}],829:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":785,"js-ext/lib/object.js":788,"js-ext/lib/string.js":790,"polyfill":807}],830:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":783,"./attribute-extractor.js":827,"./element-array.js":828,"./html-parser.js":831,"./node-parser.js":832,"./vdom-ns.js":833,"./vnode.js":834,"js-ext/extra/hashmap.js":785,"js-ext/lib/object.js":788,"js-ext/lib/promise.js":789,"js-ext/lib/string.js":790,"polyfill":807,"polyfill/extra/transition.js":802,"polyfill/extra/transitionend.js":803,"polyfill/extra/vendorCSS.js":804,"utils":808,"window-ext":814}],831:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":827,"./vdom-ns.js":833,"js-ext/extra/hashmap.js":785,"js-ext/lib/object.js":788,"polyfill":807}],832:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":827,"./vdom-ns.js":833,"./vnode.js":834,"js-ext/extra/hashmap.js":785,"js-ext/lib/object.js":788,"polyfill":807}],833:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":785,"js-ext/lib/object.js":788,"polyfill":807}],834:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":827,"./html-parser.js":831,"./vdom-ns.js":833,"js-ext/extra/hashmap.js":785,"js-ext/extra/lightmap.js":786,"js-ext/lib/array.js":787,"js-ext/lib/object.js":788,"js-ext/lib/string.js":790,"polyfill":807,"utils/lib/timers.js":810}],835:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":829,"./partials/extend-element.js":830,"./partials/node-parser.js":832,"js-ext/extra/hashmap.js":785,"js-ext/lib/object.js":788,"utils/lib/timers.js":810}],836:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":837,"./lib/timers.js":838}],837:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":842,"polyfill/polyfill-base.js":841}],838:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":841}],839:[function(require,module,exports){
module.exports=require(14)
},{}],840:[function(require,module,exports){
module.exports=require(15)
},{}],841:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":839,"./lib/window.console.js":840}],842:[function(require,module,exports){
module.exports=require(4)
},{}],843:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":842,"polyfill/polyfill-base.js":846,"utils":847}],844:[function(require,module,exports){
module.exports=require(14)
},{}],845:[function(require,module,exports){
module.exports=require(15)
},{}],846:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":844,"./lib/window.console.js":845}],847:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":848,"./lib/timers.js":849}],848:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":842,"polyfill/polyfill-base.js":852}],849:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":852}],850:[function(require,module,exports){
module.exports=require(14)
},{}],851:[function(require,module,exports){
module.exports=require(15)
},{}],852:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":850,"./lib/window.console.js":851}],853:[function(require,module,exports){
arguments[4][5][0].apply(exports,arguments)
},{"event-dom":854,"js-ext/extra/classes.js":949,"js-ext/extra/hashmap.js":950,"js-ext/lib/object.js":951,"js-ext/lib/promise.js":952,"js-ext/lib/string.js":953,"polyfill":965,"utils/lib/timers.js":966,"vdom":1022}],854:[function(require,module,exports){
module.exports=require(267)
},{"event":858,"js-ext/extra/hashmap.js":874,"js-ext/lib/array.js":875,"js-ext/lib/object.js":876,"js-ext/lib/string.js":877,"polyfill/polyfill-base.js":889,"utils":890,"vdom":948}],855:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":860,"js-ext/lib/object.js":861,"polyfill/polyfill-base.js":873}],856:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":855}],857:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":855,"js-ext/extra/classes.js":859,"js-ext/lib/object.js":861}],858:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":855,"./event-emitter.js":856,"./event-listener.js":857}],859:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":861,"js-ext/extra/hashmap.js":860,"polyfill/polyfill-base.js":864}],860:[function(require,module,exports){
module.exports=require(4)
},{}],861:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":860,"polyfill/polyfill-base.js":864,"utils":865}],862:[function(require,module,exports){
module.exports=require(14)
},{}],863:[function(require,module,exports){
module.exports=require(15)
},{}],864:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":862,"./lib/window.console.js":863}],865:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":866,"./lib/timers.js":867}],866:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":860,"polyfill/polyfill-base.js":870}],867:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":870}],868:[function(require,module,exports){
module.exports=require(14)
},{}],869:[function(require,module,exports){
module.exports=require(15)
},{}],870:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":868,"./lib/window.console.js":869}],871:[function(require,module,exports){
module.exports=require(14)
},{}],872:[function(require,module,exports){
module.exports=require(15)
},{}],873:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":871,"./lib/window.console.js":872}],874:[function(require,module,exports){
module.exports=require(4)
},{}],875:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":880}],876:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":874,"polyfill/polyfill-base.js":880,"utils":881}],877:[function(require,module,exports){
module.exports=require(29)
},{}],878:[function(require,module,exports){
module.exports=require(14)
},{}],879:[function(require,module,exports){
module.exports=require(15)
},{}],880:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":878,"./lib/window.console.js":879}],881:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":882,"./lib/timers.js":883}],882:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":874,"polyfill/polyfill-base.js":886}],883:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":886}],884:[function(require,module,exports){
module.exports=require(14)
},{}],885:[function(require,module,exports){
module.exports=require(15)
},{}],886:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":884,"./lib/window.console.js":885}],887:[function(require,module,exports){
module.exports=require(14)
},{}],888:[function(require,module,exports){
module.exports=require(15)
},{}],889:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":887,"./lib/window.console.js":888}],890:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":891,"./lib/timers.js":892}],891:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":874,"polyfill/polyfill-base.js":895}],892:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":895}],893:[function(require,module,exports){
module.exports=require(14)
},{}],894:[function(require,module,exports){
module.exports=require(15)
},{}],895:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":893,"./lib/window.console.js":894}],896:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],897:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":901,"js-ext/extra/hashmap.js":898,"polyfill/polyfill-base.js":907}],898:[function(require,module,exports){
module.exports=require(4)
},{}],899:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":900,"../lib/object.js":901,"./classes.js":897,"js-ext/extra/hashmap.js":898,"polyfill/lib/weakmap.js":905}],900:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":907}],901:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":898,"polyfill/polyfill-base.js":907,"utils":908}],902:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":907}],903:[function(require,module,exports){
module.exports=require(29)
},{}],904:[function(require,module,exports){
module.exports=require(14)
},{}],905:[function(require,module,exports){
module.exports=require(57)
},{}],906:[function(require,module,exports){
module.exports=require(15)
},{}],907:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":904,"./lib/window.console.js":906}],908:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":909,"./lib/timers.js":910}],909:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":898,"polyfill/polyfill-base.js":913}],910:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":913}],911:[function(require,module,exports){
module.exports=require(14)
},{}],912:[function(require,module,exports){
module.exports=require(15)
},{}],913:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":911,"./lib/window.console.js":912}],914:[function(require,module,exports){
module.exports=require(66)
},{}],915:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":914}],916:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":914}],917:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":914}],918:[function(require,module,exports){
module.exports=require(14)
},{}],919:[function(require,module,exports){
module.exports=require(15)
},{}],920:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":918,"./lib/window.console.js":919}],921:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":922,"./lib/timers.js":923}],922:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":898,"polyfill/polyfill-base.js":926}],923:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":926}],924:[function(require,module,exports){
module.exports=require(14)
},{}],925:[function(require,module,exports){
module.exports=require(15)
},{}],926:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":924,"./lib/window.console.js":925}],927:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":928}],928:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":929,"js-ext/lib/object.js":930}],929:[function(require,module,exports){
module.exports=require(4)
},{}],930:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":929,"polyfill/polyfill-base.js":933,"utils":934}],931:[function(require,module,exports){
module.exports=require(14)
},{}],932:[function(require,module,exports){
module.exports=require(15)
},{}],933:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":931,"./lib/window.console.js":932}],934:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":935,"./lib/timers.js":936}],935:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":929,"polyfill/polyfill-base.js":939}],936:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":939}],937:[function(require,module,exports){
module.exports=require(14)
},{}],938:[function(require,module,exports){
module.exports=require(15)
},{}],939:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":937,"./lib/window.console.js":938}],940:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":898,"js-ext/lib/object.js":901,"js-ext/lib/string.js":903,"polyfill":920,"polyfill/extra/transition.js":915,"polyfill/extra/vendorCSS.js":917}],941:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":898,"js-ext/lib/object.js":901,"polyfill":920}],942:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":898,"js-ext/lib/object.js":901,"js-ext/lib/string.js":903,"polyfill":920}],943:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":896,"./attribute-extractor.js":940,"./element-array.js":941,"./html-parser.js":944,"./node-parser.js":945,"./vdom-ns.js":946,"./vnode.js":947,"js-ext/extra/hashmap.js":898,"js-ext/lib/object.js":901,"js-ext/lib/promise.js":902,"js-ext/lib/string.js":903,"polyfill":920,"polyfill/extra/transition.js":915,"polyfill/extra/transitionend.js":916,"polyfill/extra/vendorCSS.js":917,"utils":921,"window-ext":927}],944:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":940,"./vdom-ns.js":946,"js-ext/extra/hashmap.js":898,"js-ext/lib/object.js":901,"polyfill":920}],945:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":940,"./vdom-ns.js":946,"./vnode.js":947,"js-ext/extra/hashmap.js":898,"js-ext/lib/object.js":901,"polyfill":920}],946:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":898,"js-ext/lib/object.js":901,"polyfill":920}],947:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":940,"./html-parser.js":944,"./vdom-ns.js":946,"js-ext/extra/hashmap.js":898,"js-ext/extra/lightmap.js":899,"js-ext/lib/array.js":900,"js-ext/lib/object.js":901,"js-ext/lib/string.js":903,"polyfill":920,"utils/lib/timers.js":923}],948:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":942,"./partials/extend-element.js":943,"./partials/node-parser.js":945,"js-ext/extra/hashmap.js":898,"js-ext/lib/object.js":901,"utils/lib/timers.js":923}],949:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":951,"js-ext/extra/hashmap.js":950,"polyfill/polyfill-base.js":956}],950:[function(require,module,exports){
module.exports=require(4)
},{}],951:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":950,"polyfill/polyfill-base.js":956,"utils":957}],952:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":956}],953:[function(require,module,exports){
module.exports=require(29)
},{}],954:[function(require,module,exports){
module.exports=require(14)
},{}],955:[function(require,module,exports){
module.exports=require(15)
},{}],956:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":954,"./lib/window.console.js":955}],957:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":958,"./lib/timers.js":959}],958:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":950,"polyfill/polyfill-base.js":962}],959:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":962}],960:[function(require,module,exports){
module.exports=require(14)
},{}],961:[function(require,module,exports){
module.exports=require(15)
},{}],962:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":960,"./lib/window.console.js":961}],963:[function(require,module,exports){
module.exports=require(14)
},{}],964:[function(require,module,exports){
module.exports=require(15)
},{}],965:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":963,"./lib/window.console.js":964}],966:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":969}],967:[function(require,module,exports){
module.exports=require(14)
},{}],968:[function(require,module,exports){
module.exports=require(15)
},{}],969:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":967,"./lib/window.console.js":968}],970:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],971:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":975,"js-ext/extra/hashmap.js":972,"polyfill/polyfill-base.js":981}],972:[function(require,module,exports){
module.exports=require(4)
},{}],973:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":974,"../lib/object.js":975,"./classes.js":971,"js-ext/extra/hashmap.js":972,"polyfill/lib/weakmap.js":979}],974:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":981}],975:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":972,"polyfill/polyfill-base.js":981,"utils":982}],976:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":981}],977:[function(require,module,exports){
module.exports=require(29)
},{}],978:[function(require,module,exports){
module.exports=require(14)
},{}],979:[function(require,module,exports){
module.exports=require(57)
},{}],980:[function(require,module,exports){
module.exports=require(15)
},{}],981:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":978,"./lib/window.console.js":980}],982:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":983,"./lib/timers.js":984}],983:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":972,"polyfill/polyfill-base.js":987}],984:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":987}],985:[function(require,module,exports){
module.exports=require(14)
},{}],986:[function(require,module,exports){
module.exports=require(15)
},{}],987:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":985,"./lib/window.console.js":986}],988:[function(require,module,exports){
module.exports=require(66)
},{}],989:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":988}],990:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":988}],991:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":988}],992:[function(require,module,exports){
module.exports=require(14)
},{}],993:[function(require,module,exports){
module.exports=require(15)
},{}],994:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":992,"./lib/window.console.js":993}],995:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":996,"./lib/timers.js":997}],996:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":972,"polyfill/polyfill-base.js":1000}],997:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1000}],998:[function(require,module,exports){
module.exports=require(14)
},{}],999:[function(require,module,exports){
module.exports=require(15)
},{}],1000:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":998,"./lib/window.console.js":999}],1001:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":1002}],1002:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":1003,"js-ext/lib/object.js":1004}],1003:[function(require,module,exports){
module.exports=require(4)
},{}],1004:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1003,"polyfill/polyfill-base.js":1007,"utils":1008}],1005:[function(require,module,exports){
module.exports=require(14)
},{}],1006:[function(require,module,exports){
module.exports=require(15)
},{}],1007:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1005,"./lib/window.console.js":1006}],1008:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1009,"./lib/timers.js":1010}],1009:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1003,"polyfill/polyfill-base.js":1013}],1010:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1013}],1011:[function(require,module,exports){
module.exports=require(14)
},{}],1012:[function(require,module,exports){
module.exports=require(15)
},{}],1013:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1011,"./lib/window.console.js":1012}],1014:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":972,"js-ext/lib/object.js":975,"js-ext/lib/string.js":977,"polyfill":994,"polyfill/extra/transition.js":989,"polyfill/extra/vendorCSS.js":991}],1015:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":972,"js-ext/lib/object.js":975,"polyfill":994}],1016:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":972,"js-ext/lib/object.js":975,"js-ext/lib/string.js":977,"polyfill":994}],1017:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":970,"./attribute-extractor.js":1014,"./element-array.js":1015,"./html-parser.js":1018,"./node-parser.js":1019,"./vdom-ns.js":1020,"./vnode.js":1021,"js-ext/extra/hashmap.js":972,"js-ext/lib/object.js":975,"js-ext/lib/promise.js":976,"js-ext/lib/string.js":977,"polyfill":994,"polyfill/extra/transition.js":989,"polyfill/extra/transitionend.js":990,"polyfill/extra/vendorCSS.js":991,"utils":995,"window-ext":1001}],1018:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":1014,"./vdom-ns.js":1020,"js-ext/extra/hashmap.js":972,"js-ext/lib/object.js":975,"polyfill":994}],1019:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":1014,"./vdom-ns.js":1020,"./vnode.js":1021,"js-ext/extra/hashmap.js":972,"js-ext/lib/object.js":975,"polyfill":994}],1020:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":972,"js-ext/lib/object.js":975,"polyfill":994}],1021:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":1014,"./html-parser.js":1018,"./vdom-ns.js":1020,"js-ext/extra/hashmap.js":972,"js-ext/extra/lightmap.js":973,"js-ext/lib/array.js":974,"js-ext/lib/object.js":975,"js-ext/lib/string.js":977,"polyfill":994,"utils/lib/timers.js":997}],1022:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":1016,"./partials/extend-element.js":1017,"./partials/node-parser.js":1019,"js-ext/extra/hashmap.js":972,"js-ext/lib/object.js":975,"utils/lib/timers.js":997}],1023:[function(require,module,exports){
module.exports=require(14)
},{}],1024:[function(require,module,exports){
module.exports=require(15)
},{}],1025:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1023,"./lib/window.console.js":1024}],1026:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1027,"./lib/timers.js":1028}],1027:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":842,"polyfill/polyfill-base.js":1031}],1028:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1031}],1029:[function(require,module,exports){
module.exports=require(14)
},{}],1030:[function(require,module,exports){
module.exports=require(15)
},{}],1031:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1029,"./lib/window.console.js":1030}],1032:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":1033}],1033:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":1034,"js-ext/lib/object.js":1035}],1034:[function(require,module,exports){
module.exports=require(4)
},{}],1035:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1034,"polyfill/polyfill-base.js":1038,"utils":1039}],1036:[function(require,module,exports){
module.exports=require(14)
},{}],1037:[function(require,module,exports){
module.exports=require(15)
},{}],1038:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1036,"./lib/window.console.js":1037}],1039:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1040,"./lib/timers.js":1041}],1040:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1034,"polyfill/polyfill-base.js":1044}],1041:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1044}],1042:[function(require,module,exports){
module.exports=require(14)
},{}],1043:[function(require,module,exports){
module.exports=require(15)
},{}],1044:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1042,"./lib/window.console.js":1043}],1045:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":1049,"js-ext/extra/hashmap.js":1046,"polyfill/polyfill-base.js":1054}],1046:[function(require,module,exports){
module.exports=require(4)
},{}],1047:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":1048,"../lib/object.js":1049,"./classes.js":1045,"js-ext/extra/hashmap.js":1046,"polyfill/lib/weakmap.js":1052}],1048:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":1054}],1049:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1046,"polyfill/polyfill-base.js":1054,"utils":1055}],1050:[function(require,module,exports){
module.exports=require(29)
},{}],1051:[function(require,module,exports){
module.exports=require(14)
},{}],1052:[function(require,module,exports){
module.exports=require(57)
},{}],1053:[function(require,module,exports){
module.exports=require(15)
},{}],1054:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1051,"./lib/window.console.js":1053}],1055:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1056,"./lib/timers.js":1057}],1056:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1046,"polyfill/polyfill-base.js":1060}],1057:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1060}],1058:[function(require,module,exports){
module.exports=require(14)
},{}],1059:[function(require,module,exports){
module.exports=require(15)
},{}],1060:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1058,"./lib/window.console.js":1059}],1061:[function(require,module,exports){
arguments[4][5][0].apply(exports,arguments)
},{"event-dom":1062,"js-ext/extra/classes.js":1157,"js-ext/extra/hashmap.js":1158,"js-ext/lib/object.js":1159,"js-ext/lib/promise.js":1160,"js-ext/lib/string.js":1161,"polyfill":1173,"utils/lib/timers.js":1174,"vdom":1230}],1062:[function(require,module,exports){
module.exports=require(267)
},{"event":1066,"js-ext/extra/hashmap.js":1082,"js-ext/lib/array.js":1083,"js-ext/lib/object.js":1084,"js-ext/lib/string.js":1085,"polyfill/polyfill-base.js":1097,"utils":1098,"vdom":1156}],1063:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":1068,"js-ext/lib/object.js":1069,"polyfill/polyfill-base.js":1081}],1064:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":1063}],1065:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":1063,"js-ext/extra/classes.js":1067,"js-ext/lib/object.js":1069}],1066:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":1063,"./event-emitter.js":1064,"./event-listener.js":1065}],1067:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":1069,"js-ext/extra/hashmap.js":1068,"polyfill/polyfill-base.js":1072}],1068:[function(require,module,exports){
module.exports=require(4)
},{}],1069:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1068,"polyfill/polyfill-base.js":1072,"utils":1073}],1070:[function(require,module,exports){
module.exports=require(14)
},{}],1071:[function(require,module,exports){
module.exports=require(15)
},{}],1072:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1070,"./lib/window.console.js":1071}],1073:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1074,"./lib/timers.js":1075}],1074:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1068,"polyfill/polyfill-base.js":1078}],1075:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1078}],1076:[function(require,module,exports){
module.exports=require(14)
},{}],1077:[function(require,module,exports){
module.exports=require(15)
},{}],1078:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1076,"./lib/window.console.js":1077}],1079:[function(require,module,exports){
module.exports=require(14)
},{}],1080:[function(require,module,exports){
module.exports=require(15)
},{}],1081:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1079,"./lib/window.console.js":1080}],1082:[function(require,module,exports){
module.exports=require(4)
},{}],1083:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":1088}],1084:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1082,"polyfill/polyfill-base.js":1088,"utils":1089}],1085:[function(require,module,exports){
module.exports=require(29)
},{}],1086:[function(require,module,exports){
module.exports=require(14)
},{}],1087:[function(require,module,exports){
module.exports=require(15)
},{}],1088:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1086,"./lib/window.console.js":1087}],1089:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1090,"./lib/timers.js":1091}],1090:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1082,"polyfill/polyfill-base.js":1094}],1091:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1094}],1092:[function(require,module,exports){
module.exports=require(14)
},{}],1093:[function(require,module,exports){
module.exports=require(15)
},{}],1094:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1092,"./lib/window.console.js":1093}],1095:[function(require,module,exports){
module.exports=require(14)
},{}],1096:[function(require,module,exports){
module.exports=require(15)
},{}],1097:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1095,"./lib/window.console.js":1096}],1098:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1099,"./lib/timers.js":1100}],1099:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1082,"polyfill/polyfill-base.js":1103}],1100:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1103}],1101:[function(require,module,exports){
module.exports=require(14)
},{}],1102:[function(require,module,exports){
module.exports=require(15)
},{}],1103:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1101,"./lib/window.console.js":1102}],1104:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],1105:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":1109,"js-ext/extra/hashmap.js":1106,"polyfill/polyfill-base.js":1115}],1106:[function(require,module,exports){
module.exports=require(4)
},{}],1107:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":1108,"../lib/object.js":1109,"./classes.js":1105,"js-ext/extra/hashmap.js":1106,"polyfill/lib/weakmap.js":1113}],1108:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":1115}],1109:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1106,"polyfill/polyfill-base.js":1115,"utils":1116}],1110:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":1115}],1111:[function(require,module,exports){
module.exports=require(29)
},{}],1112:[function(require,module,exports){
module.exports=require(14)
},{}],1113:[function(require,module,exports){
module.exports=require(57)
},{}],1114:[function(require,module,exports){
module.exports=require(15)
},{}],1115:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1112,"./lib/window.console.js":1114}],1116:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1117,"./lib/timers.js":1118}],1117:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1106,"polyfill/polyfill-base.js":1121}],1118:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1121}],1119:[function(require,module,exports){
module.exports=require(14)
},{}],1120:[function(require,module,exports){
module.exports=require(15)
},{}],1121:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1119,"./lib/window.console.js":1120}],1122:[function(require,module,exports){
module.exports=require(66)
},{}],1123:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":1122}],1124:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":1122}],1125:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":1122}],1126:[function(require,module,exports){
module.exports=require(14)
},{}],1127:[function(require,module,exports){
module.exports=require(15)
},{}],1128:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1126,"./lib/window.console.js":1127}],1129:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1130,"./lib/timers.js":1131}],1130:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1106,"polyfill/polyfill-base.js":1134}],1131:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1134}],1132:[function(require,module,exports){
module.exports=require(14)
},{}],1133:[function(require,module,exports){
module.exports=require(15)
},{}],1134:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1132,"./lib/window.console.js":1133}],1135:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":1136}],1136:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":1137,"js-ext/lib/object.js":1138}],1137:[function(require,module,exports){
module.exports=require(4)
},{}],1138:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1137,"polyfill/polyfill-base.js":1141,"utils":1142}],1139:[function(require,module,exports){
module.exports=require(14)
},{}],1140:[function(require,module,exports){
module.exports=require(15)
},{}],1141:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1139,"./lib/window.console.js":1140}],1142:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1143,"./lib/timers.js":1144}],1143:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1137,"polyfill/polyfill-base.js":1147}],1144:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1147}],1145:[function(require,module,exports){
module.exports=require(14)
},{}],1146:[function(require,module,exports){
module.exports=require(15)
},{}],1147:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1145,"./lib/window.console.js":1146}],1148:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":1106,"js-ext/lib/object.js":1109,"js-ext/lib/string.js":1111,"polyfill":1128,"polyfill/extra/transition.js":1123,"polyfill/extra/vendorCSS.js":1125}],1149:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":1106,"js-ext/lib/object.js":1109,"polyfill":1128}],1150:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":1106,"js-ext/lib/object.js":1109,"js-ext/lib/string.js":1111,"polyfill":1128}],1151:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":1104,"./attribute-extractor.js":1148,"./element-array.js":1149,"./html-parser.js":1152,"./node-parser.js":1153,"./vdom-ns.js":1154,"./vnode.js":1155,"js-ext/extra/hashmap.js":1106,"js-ext/lib/object.js":1109,"js-ext/lib/promise.js":1110,"js-ext/lib/string.js":1111,"polyfill":1128,"polyfill/extra/transition.js":1123,"polyfill/extra/transitionend.js":1124,"polyfill/extra/vendorCSS.js":1125,"utils":1129,"window-ext":1135}],1152:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":1148,"./vdom-ns.js":1154,"js-ext/extra/hashmap.js":1106,"js-ext/lib/object.js":1109,"polyfill":1128}],1153:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":1148,"./vdom-ns.js":1154,"./vnode.js":1155,"js-ext/extra/hashmap.js":1106,"js-ext/lib/object.js":1109,"polyfill":1128}],1154:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":1106,"js-ext/lib/object.js":1109,"polyfill":1128}],1155:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":1148,"./html-parser.js":1152,"./vdom-ns.js":1154,"js-ext/extra/hashmap.js":1106,"js-ext/extra/lightmap.js":1107,"js-ext/lib/array.js":1108,"js-ext/lib/object.js":1109,"js-ext/lib/string.js":1111,"polyfill":1128,"utils/lib/timers.js":1131}],1156:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":1150,"./partials/extend-element.js":1151,"./partials/node-parser.js":1153,"js-ext/extra/hashmap.js":1106,"js-ext/lib/object.js":1109,"utils/lib/timers.js":1131}],1157:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":1159,"js-ext/extra/hashmap.js":1158,"polyfill/polyfill-base.js":1164}],1158:[function(require,module,exports){
module.exports=require(4)
},{}],1159:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1158,"polyfill/polyfill-base.js":1164,"utils":1165}],1160:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":1164}],1161:[function(require,module,exports){
module.exports=require(29)
},{}],1162:[function(require,module,exports){
module.exports=require(14)
},{}],1163:[function(require,module,exports){
module.exports=require(15)
},{}],1164:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1162,"./lib/window.console.js":1163}],1165:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1166,"./lib/timers.js":1167}],1166:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1158,"polyfill/polyfill-base.js":1170}],1167:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1170}],1168:[function(require,module,exports){
module.exports=require(14)
},{}],1169:[function(require,module,exports){
module.exports=require(15)
},{}],1170:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1168,"./lib/window.console.js":1169}],1171:[function(require,module,exports){
module.exports=require(14)
},{}],1172:[function(require,module,exports){
module.exports=require(15)
},{}],1173:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1171,"./lib/window.console.js":1172}],1174:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1177}],1175:[function(require,module,exports){
module.exports=require(14)
},{}],1176:[function(require,module,exports){
module.exports=require(15)
},{}],1177:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1175,"./lib/window.console.js":1176}],1178:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],1179:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":1183,"js-ext/extra/hashmap.js":1180,"polyfill/polyfill-base.js":1189}],1180:[function(require,module,exports){
module.exports=require(4)
},{}],1181:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":1182,"../lib/object.js":1183,"./classes.js":1179,"js-ext/extra/hashmap.js":1180,"polyfill/lib/weakmap.js":1187}],1182:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":1189}],1183:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1180,"polyfill/polyfill-base.js":1189,"utils":1190}],1184:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":1189}],1185:[function(require,module,exports){
module.exports=require(29)
},{}],1186:[function(require,module,exports){
module.exports=require(14)
},{}],1187:[function(require,module,exports){
module.exports=require(57)
},{}],1188:[function(require,module,exports){
module.exports=require(15)
},{}],1189:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1186,"./lib/window.console.js":1188}],1190:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1191,"./lib/timers.js":1192}],1191:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1180,"polyfill/polyfill-base.js":1195}],1192:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1195}],1193:[function(require,module,exports){
module.exports=require(14)
},{}],1194:[function(require,module,exports){
module.exports=require(15)
},{}],1195:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1193,"./lib/window.console.js":1194}],1196:[function(require,module,exports){
module.exports=require(66)
},{}],1197:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":1196}],1198:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":1196}],1199:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":1196}],1200:[function(require,module,exports){
module.exports=require(14)
},{}],1201:[function(require,module,exports){
module.exports=require(15)
},{}],1202:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1200,"./lib/window.console.js":1201}],1203:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1204,"./lib/timers.js":1205}],1204:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1180,"polyfill/polyfill-base.js":1208}],1205:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1208}],1206:[function(require,module,exports){
module.exports=require(14)
},{}],1207:[function(require,module,exports){
module.exports=require(15)
},{}],1208:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1206,"./lib/window.console.js":1207}],1209:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":1210}],1210:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":1211,"js-ext/lib/object.js":1212}],1211:[function(require,module,exports){
module.exports=require(4)
},{}],1212:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1211,"polyfill/polyfill-base.js":1215,"utils":1216}],1213:[function(require,module,exports){
module.exports=require(14)
},{}],1214:[function(require,module,exports){
module.exports=require(15)
},{}],1215:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1213,"./lib/window.console.js":1214}],1216:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1217,"./lib/timers.js":1218}],1217:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1211,"polyfill/polyfill-base.js":1221}],1218:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1221}],1219:[function(require,module,exports){
module.exports=require(14)
},{}],1220:[function(require,module,exports){
module.exports=require(15)
},{}],1221:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1219,"./lib/window.console.js":1220}],1222:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":1180,"js-ext/lib/object.js":1183,"js-ext/lib/string.js":1185,"polyfill":1202,"polyfill/extra/transition.js":1197,"polyfill/extra/vendorCSS.js":1199}],1223:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":1180,"js-ext/lib/object.js":1183,"polyfill":1202}],1224:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":1180,"js-ext/lib/object.js":1183,"js-ext/lib/string.js":1185,"polyfill":1202}],1225:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":1178,"./attribute-extractor.js":1222,"./element-array.js":1223,"./html-parser.js":1226,"./node-parser.js":1227,"./vdom-ns.js":1228,"./vnode.js":1229,"js-ext/extra/hashmap.js":1180,"js-ext/lib/object.js":1183,"js-ext/lib/promise.js":1184,"js-ext/lib/string.js":1185,"polyfill":1202,"polyfill/extra/transition.js":1197,"polyfill/extra/transitionend.js":1198,"polyfill/extra/vendorCSS.js":1199,"utils":1203,"window-ext":1209}],1226:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":1222,"./vdom-ns.js":1228,"js-ext/extra/hashmap.js":1180,"js-ext/lib/object.js":1183,"polyfill":1202}],1227:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":1222,"./vdom-ns.js":1228,"./vnode.js":1229,"js-ext/extra/hashmap.js":1180,"js-ext/lib/object.js":1183,"polyfill":1202}],1228:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":1180,"js-ext/lib/object.js":1183,"polyfill":1202}],1229:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":1222,"./html-parser.js":1226,"./vdom-ns.js":1228,"js-ext/extra/hashmap.js":1180,"js-ext/extra/lightmap.js":1181,"js-ext/lib/array.js":1182,"js-ext/lib/object.js":1183,"js-ext/lib/string.js":1185,"polyfill":1202,"utils/lib/timers.js":1205}],1230:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":1224,"./partials/extend-element.js":1225,"./partials/node-parser.js":1227,"js-ext/extra/hashmap.js":1180,"js-ext/lib/object.js":1183,"utils/lib/timers.js":1205}],1231:[function(require,module,exports){
module.exports=require(14)
},{}],1232:[function(require,module,exports){
module.exports=require(15)
},{}],1233:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1231,"./lib/window.console.js":1232}],1234:[function(require,module,exports){
var css = "[plugin-scroll=\"true\"] {\n overflow: hidden !important;\n}\n\n[plugin-scroll=\"true\"] >span.itsa-vscroll-cont,\n[plugin-scroll=\"true\"] >span.itsa-hscroll-cont {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n position: absolute;\n display: block;\n left: -9999px;\n top: -9999px;\n opacity: 0;\n}\n\n[plugin-scroll=\"true\"]:not(.disabled) >span.itsa-vscroll-cont.itsa-visible {\n opacity: 1;\n width: 0.8em;\n height: 100%;\n right: 0;\n top: 0;\n left: auto;\n}\n\n[plugin-scroll=\"true\"]:not(.disabled) >span.itsa-hscroll-cont.itsa-visible {\n opacity: 1;\n height: 0.8em;\n width: 100%;\n left: 0;\n bottom: 0;\n top: auto;\n}\n\n[plugin-scroll=\"true\"] >span.itsa-vscroll-cont span {\n position: relative;\n display: block;\n width: 100%;\n min-height: 0.5em;\n background-color: rgba(0, 0, 0, 0.5);\n border-radius: 0.3em;\n}\n\n[plugin-scroll=\"true\"] >span.itsa-hscroll-cont span {\n position: relative;\n display: block;\n height: 100%;\n min-width: 0.5em;\n background-color: rgba(0, 0, 0, 0.5);\n border-radius: 0.3em;\n}\n\n[plugin-scroll=\"true\"][scroll-light=\"true\"] >span.itsa-vscroll-cont span,\n[plugin-scroll=\"true\"][scroll-light=\"true\"] >span.itsa-hscroll-cont span {\n background-color: rgba(255, 255, 255, 0.5);\n}\n\n[plugin-scroll=\"true\"] >span span.dd-dragging {\n cursor: default;\n}\n"; (require("/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify"))(css); module.exports = css;
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],1235:[function(require,module,exports){
module.exports=require(265)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],1236:[function(require,module,exports){
module.exports=require(266)
},{"./css/drag.css":1235,"event-dom":1237,"js-ext":1333,"js-ext/extra/hashmap.js":1332,"node-plugin":1349,"polyfill":1521,"useragent":1537,"vdom":1590,"window-ext":1591}],1237:[function(require,module,exports){
module.exports=require(267)
},{"event":1241,"js-ext/extra/hashmap.js":1257,"js-ext/lib/array.js":1258,"js-ext/lib/object.js":1259,"js-ext/lib/string.js":1260,"polyfill/polyfill-base.js":1272,"utils":1273,"vdom":1331}],1238:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":1243,"js-ext/lib/object.js":1244,"polyfill/polyfill-base.js":1256}],1239:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":1238}],1240:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":1238,"js-ext/extra/classes.js":1242,"js-ext/lib/object.js":1244}],1241:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":1238,"./event-emitter.js":1239,"./event-listener.js":1240}],1242:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":1244,"js-ext/extra/hashmap.js":1243,"polyfill/polyfill-base.js":1247}],1243:[function(require,module,exports){
module.exports=require(4)
},{}],1244:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1243,"polyfill/polyfill-base.js":1247,"utils":1248}],1245:[function(require,module,exports){
module.exports=require(14)
},{}],1246:[function(require,module,exports){
module.exports=require(15)
},{}],1247:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1245,"./lib/window.console.js":1246}],1248:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1249,"./lib/timers.js":1250}],1249:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1243,"polyfill/polyfill-base.js":1253}],1250:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1253}],1251:[function(require,module,exports){
module.exports=require(14)
},{}],1252:[function(require,module,exports){
module.exports=require(15)
},{}],1253:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1251,"./lib/window.console.js":1252}],1254:[function(require,module,exports){
module.exports=require(14)
},{}],1255:[function(require,module,exports){
module.exports=require(15)
},{}],1256:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1254,"./lib/window.console.js":1255}],1257:[function(require,module,exports){
module.exports=require(4)
},{}],1258:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":1263}],1259:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1257,"polyfill/polyfill-base.js":1263,"utils":1264}],1260:[function(require,module,exports){
module.exports=require(29)
},{}],1261:[function(require,module,exports){
module.exports=require(14)
},{}],1262:[function(require,module,exports){
module.exports=require(15)
},{}],1263:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1261,"./lib/window.console.js":1262}],1264:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1265,"./lib/timers.js":1266}],1265:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1257,"polyfill/polyfill-base.js":1269}],1266:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1269}],1267:[function(require,module,exports){
module.exports=require(14)
},{}],1268:[function(require,module,exports){
module.exports=require(15)
},{}],1269:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1267,"./lib/window.console.js":1268}],1270:[function(require,module,exports){
module.exports=require(14)
},{}],1271:[function(require,module,exports){
module.exports=require(15)
},{}],1272:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1270,"./lib/window.console.js":1271}],1273:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1274,"./lib/timers.js":1275}],1274:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1257,"polyfill/polyfill-base.js":1278}],1275:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1278}],1276:[function(require,module,exports){
module.exports=require(14)
},{}],1277:[function(require,module,exports){
module.exports=require(15)
},{}],1278:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1276,"./lib/window.console.js":1277}],1279:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],1280:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":1284,"js-ext/extra/hashmap.js":1281,"polyfill/polyfill-base.js":1290}],1281:[function(require,module,exports){
module.exports=require(4)
},{}],1282:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":1283,"../lib/object.js":1284,"./classes.js":1280,"js-ext/extra/hashmap.js":1281,"polyfill/lib/weakmap.js":1288}],1283:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":1290}],1284:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1281,"polyfill/polyfill-base.js":1290,"utils":1291}],1285:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":1290}],1286:[function(require,module,exports){
module.exports=require(29)
},{}],1287:[function(require,module,exports){
module.exports=require(14)
},{}],1288:[function(require,module,exports){
module.exports=require(57)
},{}],1289:[function(require,module,exports){
module.exports=require(15)
},{}],1290:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1287,"./lib/window.console.js":1289}],1291:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1292,"./lib/timers.js":1293}],1292:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1281,"polyfill/polyfill-base.js":1296}],1293:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1296}],1294:[function(require,module,exports){
module.exports=require(14)
},{}],1295:[function(require,module,exports){
module.exports=require(15)
},{}],1296:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1294,"./lib/window.console.js":1295}],1297:[function(require,module,exports){
module.exports=require(66)
},{}],1298:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":1297}],1299:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":1297}],1300:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":1297}],1301:[function(require,module,exports){
module.exports=require(14)
},{}],1302:[function(require,module,exports){
module.exports=require(15)
},{}],1303:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1301,"./lib/window.console.js":1302}],1304:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1305,"./lib/timers.js":1306}],1305:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1281,"polyfill/polyfill-base.js":1309}],1306:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1309}],1307:[function(require,module,exports){
module.exports=require(14)
},{}],1308:[function(require,module,exports){
module.exports=require(15)
},{}],1309:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1307,"./lib/window.console.js":1308}],1310:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":1311}],1311:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":1312,"js-ext/lib/object.js":1313}],1312:[function(require,module,exports){
module.exports=require(4)
},{}],1313:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1312,"polyfill/polyfill-base.js":1316,"utils":1317}],1314:[function(require,module,exports){
module.exports=require(14)
},{}],1315:[function(require,module,exports){
module.exports=require(15)
},{}],1316:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1314,"./lib/window.console.js":1315}],1317:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1318,"./lib/timers.js":1319}],1318:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1312,"polyfill/polyfill-base.js":1322}],1319:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1322}],1320:[function(require,module,exports){
module.exports=require(14)
},{}],1321:[function(require,module,exports){
module.exports=require(15)
},{}],1322:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1320,"./lib/window.console.js":1321}],1323:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":1281,"js-ext/lib/object.js":1284,"js-ext/lib/string.js":1286,"polyfill":1303,"polyfill/extra/transition.js":1298,"polyfill/extra/vendorCSS.js":1300}],1324:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":1281,"js-ext/lib/object.js":1284,"polyfill":1303}],1325:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":1281,"js-ext/lib/object.js":1284,"js-ext/lib/string.js":1286,"polyfill":1303}],1326:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":1279,"./attribute-extractor.js":1323,"./element-array.js":1324,"./html-parser.js":1327,"./node-parser.js":1328,"./vdom-ns.js":1329,"./vnode.js":1330,"js-ext/extra/hashmap.js":1281,"js-ext/lib/object.js":1284,"js-ext/lib/promise.js":1285,"js-ext/lib/string.js":1286,"polyfill":1303,"polyfill/extra/transition.js":1298,"polyfill/extra/transitionend.js":1299,"polyfill/extra/vendorCSS.js":1300,"utils":1304,"window-ext":1310}],1327:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":1323,"./vdom-ns.js":1329,"js-ext/extra/hashmap.js":1281,"js-ext/lib/object.js":1284,"polyfill":1303}],1328:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":1323,"./vdom-ns.js":1329,"./vnode.js":1330,"js-ext/extra/hashmap.js":1281,"js-ext/lib/object.js":1284,"polyfill":1303}],1329:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":1281,"js-ext/lib/object.js":1284,"polyfill":1303}],1330:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":1323,"./html-parser.js":1327,"./vdom-ns.js":1329,"js-ext/extra/hashmap.js":1281,"js-ext/extra/lightmap.js":1282,"js-ext/lib/array.js":1283,"js-ext/lib/object.js":1284,"js-ext/lib/string.js":1286,"polyfill":1303,"utils/lib/timers.js":1306}],1331:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":1325,"./partials/extend-element.js":1326,"./partials/node-parser.js":1328,"js-ext/extra/hashmap.js":1281,"js-ext/lib/object.js":1284,"utils/lib/timers.js":1306}],1332:[function(require,module,exports){
module.exports=require(4)
},{}],1333:[function(require,module,exports){
module.exports=require(202)
},{"./lib/array.js":1334,"./lib/function.js":1335,"./lib/json.js":1336,"./lib/object.js":1337,"./lib/promise.js":1338,"./lib/string.js":1339}],1334:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":1342}],1335:[function(require,module,exports){
module.exports=require(204)
},{"polyfill/polyfill-base.js":1342}],1336:[function(require,module,exports){
module.exports=require(205)
},{"polyfill/polyfill-base.js":1342}],1337:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1332,"polyfill/polyfill-base.js":1342,"utils":1343}],1338:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":1342}],1339:[function(require,module,exports){
module.exports=require(29)
},{}],1340:[function(require,module,exports){
module.exports=require(14)
},{}],1341:[function(require,module,exports){
module.exports=require(15)
},{}],1342:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1340,"./lib/window.console.js":1341}],1343:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1344,"./lib/timers.js":1345}],1344:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1332,"polyfill/polyfill-base.js":1348}],1345:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1348}],1346:[function(require,module,exports){
module.exports=require(14)
},{}],1347:[function(require,module,exports){
module.exports=require(15)
},{}],1348:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1346,"./lib/window.console.js":1347}],1349:[function(require,module,exports){
arguments[4][5][0].apply(exports,arguments)
},{"event-dom":1350,"js-ext/extra/classes.js":1445,"js-ext/extra/hashmap.js":1446,"js-ext/lib/object.js":1447,"js-ext/lib/promise.js":1448,"js-ext/lib/string.js":1449,"polyfill":1461,"utils/lib/timers.js":1462,"vdom":1518}],1350:[function(require,module,exports){
module.exports=require(267)
},{"event":1354,"js-ext/extra/hashmap.js":1370,"js-ext/lib/array.js":1371,"js-ext/lib/object.js":1372,"js-ext/lib/string.js":1373,"polyfill/polyfill-base.js":1385,"utils":1386,"vdom":1444}],1351:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":1356,"js-ext/lib/object.js":1357,"polyfill/polyfill-base.js":1369}],1352:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":1351}],1353:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":1351,"js-ext/extra/classes.js":1355,"js-ext/lib/object.js":1357}],1354:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":1351,"./event-emitter.js":1352,"./event-listener.js":1353}],1355:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":1357,"js-ext/extra/hashmap.js":1356,"polyfill/polyfill-base.js":1360}],1356:[function(require,module,exports){
module.exports=require(4)
},{}],1357:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1356,"polyfill/polyfill-base.js":1360,"utils":1361}],1358:[function(require,module,exports){
module.exports=require(14)
},{}],1359:[function(require,module,exports){
module.exports=require(15)
},{}],1360:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1358,"./lib/window.console.js":1359}],1361:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1362,"./lib/timers.js":1363}],1362:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1356,"polyfill/polyfill-base.js":1366}],1363:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1366}],1364:[function(require,module,exports){
module.exports=require(14)
},{}],1365:[function(require,module,exports){
module.exports=require(15)
},{}],1366:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1364,"./lib/window.console.js":1365}],1367:[function(require,module,exports){
module.exports=require(14)
},{}],1368:[function(require,module,exports){
module.exports=require(15)
},{}],1369:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1367,"./lib/window.console.js":1368}],1370:[function(require,module,exports){
module.exports=require(4)
},{}],1371:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":1376}],1372:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1370,"polyfill/polyfill-base.js":1376,"utils":1377}],1373:[function(require,module,exports){
module.exports=require(29)
},{}],1374:[function(require,module,exports){
module.exports=require(14)
},{}],1375:[function(require,module,exports){
module.exports=require(15)
},{}],1376:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1374,"./lib/window.console.js":1375}],1377:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1378,"./lib/timers.js":1379}],1378:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1370,"polyfill/polyfill-base.js":1382}],1379:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1382}],1380:[function(require,module,exports){
module.exports=require(14)
},{}],1381:[function(require,module,exports){
module.exports=require(15)
},{}],1382:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1380,"./lib/window.console.js":1381}],1383:[function(require,module,exports){
module.exports=require(14)
},{}],1384:[function(require,module,exports){
module.exports=require(15)
},{}],1385:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1383,"./lib/window.console.js":1384}],1386:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1387,"./lib/timers.js":1388}],1387:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1370,"polyfill/polyfill-base.js":1391}],1388:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1391}],1389:[function(require,module,exports){
module.exports=require(14)
},{}],1390:[function(require,module,exports){
module.exports=require(15)
},{}],1391:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1389,"./lib/window.console.js":1390}],1392:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],1393:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":1397,"js-ext/extra/hashmap.js":1394,"polyfill/polyfill-base.js":1403}],1394:[function(require,module,exports){
module.exports=require(4)
},{}],1395:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":1396,"../lib/object.js":1397,"./classes.js":1393,"js-ext/extra/hashmap.js":1394,"polyfill/lib/weakmap.js":1401}],1396:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":1403}],1397:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1394,"polyfill/polyfill-base.js":1403,"utils":1404}],1398:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":1403}],1399:[function(require,module,exports){
module.exports=require(29)
},{}],1400:[function(require,module,exports){
module.exports=require(14)
},{}],1401:[function(require,module,exports){
module.exports=require(57)
},{}],1402:[function(require,module,exports){
module.exports=require(15)
},{}],1403:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1400,"./lib/window.console.js":1402}],1404:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1405,"./lib/timers.js":1406}],1405:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1394,"polyfill/polyfill-base.js":1409}],1406:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1409}],1407:[function(require,module,exports){
module.exports=require(14)
},{}],1408:[function(require,module,exports){
module.exports=require(15)
},{}],1409:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1407,"./lib/window.console.js":1408}],1410:[function(require,module,exports){
module.exports=require(66)
},{}],1411:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":1410}],1412:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":1410}],1413:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":1410}],1414:[function(require,module,exports){
module.exports=require(14)
},{}],1415:[function(require,module,exports){
module.exports=require(15)
},{}],1416:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1414,"./lib/window.console.js":1415}],1417:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1418,"./lib/timers.js":1419}],1418:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1394,"polyfill/polyfill-base.js":1422}],1419:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1422}],1420:[function(require,module,exports){
module.exports=require(14)
},{}],1421:[function(require,module,exports){
module.exports=require(15)
},{}],1422:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1420,"./lib/window.console.js":1421}],1423:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":1424}],1424:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":1425,"js-ext/lib/object.js":1426}],1425:[function(require,module,exports){
module.exports=require(4)
},{}],1426:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1425,"polyfill/polyfill-base.js":1429,"utils":1430}],1427:[function(require,module,exports){
module.exports=require(14)
},{}],1428:[function(require,module,exports){
module.exports=require(15)
},{}],1429:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1427,"./lib/window.console.js":1428}],1430:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1431,"./lib/timers.js":1432}],1431:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1425,"polyfill/polyfill-base.js":1435}],1432:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1435}],1433:[function(require,module,exports){
module.exports=require(14)
},{}],1434:[function(require,module,exports){
module.exports=require(15)
},{}],1435:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1433,"./lib/window.console.js":1434}],1436:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":1394,"js-ext/lib/object.js":1397,"js-ext/lib/string.js":1399,"polyfill":1416,"polyfill/extra/transition.js":1411,"polyfill/extra/vendorCSS.js":1413}],1437:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":1394,"js-ext/lib/object.js":1397,"polyfill":1416}],1438:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":1394,"js-ext/lib/object.js":1397,"js-ext/lib/string.js":1399,"polyfill":1416}],1439:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":1392,"./attribute-extractor.js":1436,"./element-array.js":1437,"./html-parser.js":1440,"./node-parser.js":1441,"./vdom-ns.js":1442,"./vnode.js":1443,"js-ext/extra/hashmap.js":1394,"js-ext/lib/object.js":1397,"js-ext/lib/promise.js":1398,"js-ext/lib/string.js":1399,"polyfill":1416,"polyfill/extra/transition.js":1411,"polyfill/extra/transitionend.js":1412,"polyfill/extra/vendorCSS.js":1413,"utils":1417,"window-ext":1423}],1440:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":1436,"./vdom-ns.js":1442,"js-ext/extra/hashmap.js":1394,"js-ext/lib/object.js":1397,"polyfill":1416}],1441:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":1436,"./vdom-ns.js":1442,"./vnode.js":1443,"js-ext/extra/hashmap.js":1394,"js-ext/lib/object.js":1397,"polyfill":1416}],1442:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":1394,"js-ext/lib/object.js":1397,"polyfill":1416}],1443:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":1436,"./html-parser.js":1440,"./vdom-ns.js":1442,"js-ext/extra/hashmap.js":1394,"js-ext/extra/lightmap.js":1395,"js-ext/lib/array.js":1396,"js-ext/lib/object.js":1397,"js-ext/lib/string.js":1399,"polyfill":1416,"utils/lib/timers.js":1419}],1444:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":1438,"./partials/extend-element.js":1439,"./partials/node-parser.js":1441,"js-ext/extra/hashmap.js":1394,"js-ext/lib/object.js":1397,"utils/lib/timers.js":1419}],1445:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":1447,"js-ext/extra/hashmap.js":1446,"polyfill/polyfill-base.js":1452}],1446:[function(require,module,exports){
module.exports=require(4)
},{}],1447:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1446,"polyfill/polyfill-base.js":1452,"utils":1453}],1448:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":1452}],1449:[function(require,module,exports){
module.exports=require(29)
},{}],1450:[function(require,module,exports){
module.exports=require(14)
},{}],1451:[function(require,module,exports){
module.exports=require(15)
},{}],1452:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1450,"./lib/window.console.js":1451}],1453:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1454,"./lib/timers.js":1455}],1454:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1446,"polyfill/polyfill-base.js":1458}],1455:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1458}],1456:[function(require,module,exports){
module.exports=require(14)
},{}],1457:[function(require,module,exports){
module.exports=require(15)
},{}],1458:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1456,"./lib/window.console.js":1457}],1459:[function(require,module,exports){
module.exports=require(14)
},{}],1460:[function(require,module,exports){
module.exports=require(15)
},{}],1461:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1459,"./lib/window.console.js":1460}],1462:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1465}],1463:[function(require,module,exports){
module.exports=require(14)
},{}],1464:[function(require,module,exports){
module.exports=require(15)
},{}],1465:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1463,"./lib/window.console.js":1464}],1466:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],1467:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":1471,"js-ext/extra/hashmap.js":1468,"polyfill/polyfill-base.js":1477}],1468:[function(require,module,exports){
module.exports=require(4)
},{}],1469:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":1470,"../lib/object.js":1471,"./classes.js":1467,"js-ext/extra/hashmap.js":1468,"polyfill/lib/weakmap.js":1475}],1470:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":1477}],1471:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1468,"polyfill/polyfill-base.js":1477,"utils":1478}],1472:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":1477}],1473:[function(require,module,exports){
module.exports=require(29)
},{}],1474:[function(require,module,exports){
module.exports=require(14)
},{}],1475:[function(require,module,exports){
module.exports=require(57)
},{}],1476:[function(require,module,exports){
module.exports=require(15)
},{}],1477:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1474,"./lib/window.console.js":1476}],1478:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1479,"./lib/timers.js":1480}],1479:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1468,"polyfill/polyfill-base.js":1483}],1480:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1483}],1481:[function(require,module,exports){
module.exports=require(14)
},{}],1482:[function(require,module,exports){
module.exports=require(15)
},{}],1483:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1481,"./lib/window.console.js":1482}],1484:[function(require,module,exports){
module.exports=require(66)
},{}],1485:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":1484}],1486:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":1484}],1487:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":1484}],1488:[function(require,module,exports){
module.exports=require(14)
},{}],1489:[function(require,module,exports){
module.exports=require(15)
},{}],1490:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1488,"./lib/window.console.js":1489}],1491:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1492,"./lib/timers.js":1493}],1492:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1468,"polyfill/polyfill-base.js":1496}],1493:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1496}],1494:[function(require,module,exports){
module.exports=require(14)
},{}],1495:[function(require,module,exports){
module.exports=require(15)
},{}],1496:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1494,"./lib/window.console.js":1495}],1497:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":1498}],1498:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":1499,"js-ext/lib/object.js":1500}],1499:[function(require,module,exports){
module.exports=require(4)
},{}],1500:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1499,"polyfill/polyfill-base.js":1503,"utils":1504}],1501:[function(require,module,exports){
module.exports=require(14)
},{}],1502:[function(require,module,exports){
module.exports=require(15)
},{}],1503:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1501,"./lib/window.console.js":1502}],1504:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1505,"./lib/timers.js":1506}],1505:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1499,"polyfill/polyfill-base.js":1509}],1506:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1509}],1507:[function(require,module,exports){
module.exports=require(14)
},{}],1508:[function(require,module,exports){
module.exports=require(15)
},{}],1509:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1507,"./lib/window.console.js":1508}],1510:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":1468,"js-ext/lib/object.js":1471,"js-ext/lib/string.js":1473,"polyfill":1490,"polyfill/extra/transition.js":1485,"polyfill/extra/vendorCSS.js":1487}],1511:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":1468,"js-ext/lib/object.js":1471,"polyfill":1490}],1512:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":1468,"js-ext/lib/object.js":1471,"js-ext/lib/string.js":1473,"polyfill":1490}],1513:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":1466,"./attribute-extractor.js":1510,"./element-array.js":1511,"./html-parser.js":1514,"./node-parser.js":1515,"./vdom-ns.js":1516,"./vnode.js":1517,"js-ext/extra/hashmap.js":1468,"js-ext/lib/object.js":1471,"js-ext/lib/promise.js":1472,"js-ext/lib/string.js":1473,"polyfill":1490,"polyfill/extra/transition.js":1485,"polyfill/extra/transitionend.js":1486,"polyfill/extra/vendorCSS.js":1487,"utils":1491,"window-ext":1497}],1514:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":1510,"./vdom-ns.js":1516,"js-ext/extra/hashmap.js":1468,"js-ext/lib/object.js":1471,"polyfill":1490}],1515:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":1510,"./vdom-ns.js":1516,"./vnode.js":1517,"js-ext/extra/hashmap.js":1468,"js-ext/lib/object.js":1471,"polyfill":1490}],1516:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":1468,"js-ext/lib/object.js":1471,"polyfill":1490}],1517:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":1510,"./html-parser.js":1514,"./vdom-ns.js":1516,"js-ext/extra/hashmap.js":1468,"js-ext/extra/lightmap.js":1469,"js-ext/lib/array.js":1470,"js-ext/lib/object.js":1471,"js-ext/lib/string.js":1473,"polyfill":1490,"utils/lib/timers.js":1493}],1518:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":1512,"./partials/extend-element.js":1513,"./partials/node-parser.js":1515,"js-ext/extra/hashmap.js":1468,"js-ext/lib/object.js":1471,"utils/lib/timers.js":1493}],1519:[function(require,module,exports){
module.exports=require(14)
},{}],1520:[function(require,module,exports){
module.exports=require(15)
},{}],1521:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1519,"./lib/window.console.js":1520}],1522:[function(require,module,exports){
module.exports=require(4)
},{}],1523:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1522,"polyfill/polyfill-base.js":1527,"utils":1528}],1524:[function(require,module,exports){
module.exports=require(29)
},{}],1525:[function(require,module,exports){
module.exports=require(14)
},{}],1526:[function(require,module,exports){
module.exports=require(15)
},{}],1527:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1525,"./lib/window.console.js":1526}],1528:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1529,"./lib/timers.js":1530}],1529:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1522,"polyfill/polyfill-base.js":1533}],1530:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1533}],1531:[function(require,module,exports){
module.exports=require(14)
},{}],1532:[function(require,module,exports){
module.exports=require(15)
},{}],1533:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1531,"./lib/window.console.js":1532}],1534:[function(require,module,exports){
module.exports=require(14)
},{}],1535:[function(require,module,exports){
module.exports=require(15)
},{}],1536:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1534,"./lib/window.console.js":1535}],1537:[function(require,module,exports){
module.exports=require(567)
},{"js-ext/extra/hashmap.js":1522,"js-ext/lib/object.js":1523,"js-ext/lib/string.js":1524,"polyfill":1536}],1538:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],1539:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":1543,"js-ext/extra/hashmap.js":1540,"polyfill/polyfill-base.js":1549}],1540:[function(require,module,exports){
module.exports=require(4)
},{}],1541:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":1542,"../lib/object.js":1543,"./classes.js":1539,"js-ext/extra/hashmap.js":1540,"polyfill/lib/weakmap.js":1547}],1542:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":1549}],1543:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1540,"polyfill/polyfill-base.js":1549,"utils":1550}],1544:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":1549}],1545:[function(require,module,exports){
module.exports=require(29)
},{}],1546:[function(require,module,exports){
module.exports=require(14)
},{}],1547:[function(require,module,exports){
module.exports=require(57)
},{}],1548:[function(require,module,exports){
module.exports=require(15)
},{}],1549:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1546,"./lib/window.console.js":1548}],1550:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1551,"./lib/timers.js":1552}],1551:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1540,"polyfill/polyfill-base.js":1555}],1552:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1555}],1553:[function(require,module,exports){
module.exports=require(14)
},{}],1554:[function(require,module,exports){
module.exports=require(15)
},{}],1555:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1553,"./lib/window.console.js":1554}],1556:[function(require,module,exports){
module.exports=require(66)
},{}],1557:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":1556}],1558:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":1556}],1559:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":1556}],1560:[function(require,module,exports){
module.exports=require(14)
},{}],1561:[function(require,module,exports){
module.exports=require(15)
},{}],1562:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1560,"./lib/window.console.js":1561}],1563:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1564,"./lib/timers.js":1565}],1564:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1540,"polyfill/polyfill-base.js":1568}],1565:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1568}],1566:[function(require,module,exports){
module.exports=require(14)
},{}],1567:[function(require,module,exports){
module.exports=require(15)
},{}],1568:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1566,"./lib/window.console.js":1567}],1569:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":1570}],1570:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":1571,"js-ext/lib/object.js":1572}],1571:[function(require,module,exports){
module.exports=require(4)
},{}],1572:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1571,"polyfill/polyfill-base.js":1575,"utils":1576}],1573:[function(require,module,exports){
module.exports=require(14)
},{}],1574:[function(require,module,exports){
module.exports=require(15)
},{}],1575:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1573,"./lib/window.console.js":1574}],1576:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1577,"./lib/timers.js":1578}],1577:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1571,"polyfill/polyfill-base.js":1581}],1578:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1581}],1579:[function(require,module,exports){
module.exports=require(14)
},{}],1580:[function(require,module,exports){
module.exports=require(15)
},{}],1581:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1579,"./lib/window.console.js":1580}],1582:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":1540,"js-ext/lib/object.js":1543,"js-ext/lib/string.js":1545,"polyfill":1562,"polyfill/extra/transition.js":1557,"polyfill/extra/vendorCSS.js":1559}],1583:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":1540,"js-ext/lib/object.js":1543,"polyfill":1562}],1584:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":1540,"js-ext/lib/object.js":1543,"js-ext/lib/string.js":1545,"polyfill":1562}],1585:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":1538,"./attribute-extractor.js":1582,"./element-array.js":1583,"./html-parser.js":1586,"./node-parser.js":1587,"./vdom-ns.js":1588,"./vnode.js":1589,"js-ext/extra/hashmap.js":1540,"js-ext/lib/object.js":1543,"js-ext/lib/promise.js":1544,"js-ext/lib/string.js":1545,"polyfill":1562,"polyfill/extra/transition.js":1557,"polyfill/extra/transitionend.js":1558,"polyfill/extra/vendorCSS.js":1559,"utils":1563,"window-ext":1569}],1586:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":1582,"./vdom-ns.js":1588,"js-ext/extra/hashmap.js":1540,"js-ext/lib/object.js":1543,"polyfill":1562}],1587:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":1582,"./vdom-ns.js":1588,"./vnode.js":1589,"js-ext/extra/hashmap.js":1540,"js-ext/lib/object.js":1543,"polyfill":1562}],1588:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":1540,"js-ext/lib/object.js":1543,"polyfill":1562}],1589:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":1582,"./html-parser.js":1586,"./vdom-ns.js":1588,"js-ext/extra/hashmap.js":1540,"js-ext/extra/lightmap.js":1541,"js-ext/lib/array.js":1542,"js-ext/lib/object.js":1543,"js-ext/lib/string.js":1545,"polyfill":1562,"utils/lib/timers.js":1565}],1590:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":1584,"./partials/extend-element.js":1585,"./partials/node-parser.js":1587,"js-ext/extra/hashmap.js":1540,"js-ext/lib/object.js":1543,"utils/lib/timers.js":1565}],1591:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":1592}],1592:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":1593,"js-ext/lib/object.js":1594}],1593:[function(require,module,exports){
module.exports=require(4)
},{}],1594:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1593,"polyfill/polyfill-base.js":1597,"utils":1598}],1595:[function(require,module,exports){
module.exports=require(14)
},{}],1596:[function(require,module,exports){
module.exports=require(15)
},{}],1597:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1595,"./lib/window.console.js":1596}],1598:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1599,"./lib/timers.js":1600}],1599:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1593,"polyfill/polyfill-base.js":1603}],1600:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1603}],1601:[function(require,module,exports){
module.exports=require(14)
},{}],1602:[function(require,module,exports){
module.exports=require(15)
},{}],1603:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1601,"./lib/window.console.js":1602}],1604:[function(require,module,exports){
module.exports=require(634)
},{"./lib/hammer-2.0.4.js":1605,"event-dom":1606}],1605:[function(require,module,exports){
module.exports=require(635)
},{"utils":1701}],1606:[function(require,module,exports){
module.exports=require(267)
},{"event":1610,"js-ext/extra/hashmap.js":1626,"js-ext/lib/array.js":1627,"js-ext/lib/object.js":1628,"js-ext/lib/string.js":1629,"polyfill/polyfill-base.js":1641,"utils":1642,"vdom":1700}],1607:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":1612,"js-ext/lib/object.js":1613,"polyfill/polyfill-base.js":1625}],1608:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":1607}],1609:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":1607,"js-ext/extra/classes.js":1611,"js-ext/lib/object.js":1613}],1610:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":1607,"./event-emitter.js":1608,"./event-listener.js":1609}],1611:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":1613,"js-ext/extra/hashmap.js":1612,"polyfill/polyfill-base.js":1616}],1612:[function(require,module,exports){
module.exports=require(4)
},{}],1613:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1612,"polyfill/polyfill-base.js":1616,"utils":1617}],1614:[function(require,module,exports){
module.exports=require(14)
},{}],1615:[function(require,module,exports){
module.exports=require(15)
},{}],1616:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1614,"./lib/window.console.js":1615}],1617:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1618,"./lib/timers.js":1619}],1618:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1612,"polyfill/polyfill-base.js":1622}],1619:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1622}],1620:[function(require,module,exports){
module.exports=require(14)
},{}],1621:[function(require,module,exports){
module.exports=require(15)
},{}],1622:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1620,"./lib/window.console.js":1621}],1623:[function(require,module,exports){
module.exports=require(14)
},{}],1624:[function(require,module,exports){
module.exports=require(15)
},{}],1625:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1623,"./lib/window.console.js":1624}],1626:[function(require,module,exports){
module.exports=require(4)
},{}],1627:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":1632}],1628:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1626,"polyfill/polyfill-base.js":1632,"utils":1633}],1629:[function(require,module,exports){
module.exports=require(29)
},{}],1630:[function(require,module,exports){
module.exports=require(14)
},{}],1631:[function(require,module,exports){
module.exports=require(15)
},{}],1632:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1630,"./lib/window.console.js":1631}],1633:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1634,"./lib/timers.js":1635}],1634:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1626,"polyfill/polyfill-base.js":1638}],1635:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1638}],1636:[function(require,module,exports){
module.exports=require(14)
},{}],1637:[function(require,module,exports){
module.exports=require(15)
},{}],1638:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1636,"./lib/window.console.js":1637}],1639:[function(require,module,exports){
module.exports=require(14)
},{}],1640:[function(require,module,exports){
module.exports=require(15)
},{}],1641:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1639,"./lib/window.console.js":1640}],1642:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1643,"./lib/timers.js":1644}],1643:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1626,"polyfill/polyfill-base.js":1647}],1644:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1647}],1645:[function(require,module,exports){
module.exports=require(14)
},{}],1646:[function(require,module,exports){
module.exports=require(15)
},{}],1647:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1645,"./lib/window.console.js":1646}],1648:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],1649:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":1653,"js-ext/extra/hashmap.js":1650,"polyfill/polyfill-base.js":1659}],1650:[function(require,module,exports){
module.exports=require(4)
},{}],1651:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":1652,"../lib/object.js":1653,"./classes.js":1649,"js-ext/extra/hashmap.js":1650,"polyfill/lib/weakmap.js":1657}],1652:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":1659}],1653:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1650,"polyfill/polyfill-base.js":1659,"utils":1660}],1654:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":1659}],1655:[function(require,module,exports){
module.exports=require(29)
},{}],1656:[function(require,module,exports){
module.exports=require(14)
},{}],1657:[function(require,module,exports){
module.exports=require(57)
},{}],1658:[function(require,module,exports){
module.exports=require(15)
},{}],1659:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1656,"./lib/window.console.js":1658}],1660:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1661,"./lib/timers.js":1662}],1661:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1650,"polyfill/polyfill-base.js":1665}],1662:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1665}],1663:[function(require,module,exports){
module.exports=require(14)
},{}],1664:[function(require,module,exports){
module.exports=require(15)
},{}],1665:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1663,"./lib/window.console.js":1664}],1666:[function(require,module,exports){
module.exports=require(66)
},{}],1667:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":1666}],1668:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":1666}],1669:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":1666}],1670:[function(require,module,exports){
module.exports=require(14)
},{}],1671:[function(require,module,exports){
module.exports=require(15)
},{}],1672:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1670,"./lib/window.console.js":1671}],1673:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1674,"./lib/timers.js":1675}],1674:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1650,"polyfill/polyfill-base.js":1678}],1675:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1678}],1676:[function(require,module,exports){
module.exports=require(14)
},{}],1677:[function(require,module,exports){
module.exports=require(15)
},{}],1678:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1676,"./lib/window.console.js":1677}],1679:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":1680}],1680:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":1681,"js-ext/lib/object.js":1682}],1681:[function(require,module,exports){
module.exports=require(4)
},{}],1682:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1681,"polyfill/polyfill-base.js":1685,"utils":1686}],1683:[function(require,module,exports){
module.exports=require(14)
},{}],1684:[function(require,module,exports){
module.exports=require(15)
},{}],1685:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1683,"./lib/window.console.js":1684}],1686:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1687,"./lib/timers.js":1688}],1687:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1681,"polyfill/polyfill-base.js":1691}],1688:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1691}],1689:[function(require,module,exports){
module.exports=require(14)
},{}],1690:[function(require,module,exports){
module.exports=require(15)
},{}],1691:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1689,"./lib/window.console.js":1690}],1692:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":1650,"js-ext/lib/object.js":1653,"js-ext/lib/string.js":1655,"polyfill":1672,"polyfill/extra/transition.js":1667,"polyfill/extra/vendorCSS.js":1669}],1693:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":1650,"js-ext/lib/object.js":1653,"polyfill":1672}],1694:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":1650,"js-ext/lib/object.js":1653,"js-ext/lib/string.js":1655,"polyfill":1672}],1695:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":1648,"./attribute-extractor.js":1692,"./element-array.js":1693,"./html-parser.js":1696,"./node-parser.js":1697,"./vdom-ns.js":1698,"./vnode.js":1699,"js-ext/extra/hashmap.js":1650,"js-ext/lib/object.js":1653,"js-ext/lib/promise.js":1654,"js-ext/lib/string.js":1655,"polyfill":1672,"polyfill/extra/transition.js":1667,"polyfill/extra/transitionend.js":1668,"polyfill/extra/vendorCSS.js":1669,"utils":1673,"window-ext":1679}],1696:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":1692,"./vdom-ns.js":1698,"js-ext/extra/hashmap.js":1650,"js-ext/lib/object.js":1653,"polyfill":1672}],1697:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":1692,"./vdom-ns.js":1698,"./vnode.js":1699,"js-ext/extra/hashmap.js":1650,"js-ext/lib/object.js":1653,"polyfill":1672}],1698:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":1650,"js-ext/lib/object.js":1653,"polyfill":1672}],1699:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":1692,"./html-parser.js":1696,"./vdom-ns.js":1698,"js-ext/extra/hashmap.js":1650,"js-ext/extra/lightmap.js":1651,"js-ext/lib/array.js":1652,"js-ext/lib/object.js":1653,"js-ext/lib/string.js":1655,"polyfill":1672,"utils/lib/timers.js":1675}],1700:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":1694,"./partials/extend-element.js":1695,"./partials/node-parser.js":1697,"js-ext/extra/hashmap.js":1650,"js-ext/lib/object.js":1653,"utils/lib/timers.js":1675}],1701:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1702,"./lib/timers.js":1703}],1702:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1707,"polyfill/polyfill-base.js":1706}],1703:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1706}],1704:[function(require,module,exports){
module.exports=require(14)
},{}],1705:[function(require,module,exports){
module.exports=require(15)
},{}],1706:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1704,"./lib/window.console.js":1705}],1707:[function(require,module,exports){
module.exports=require(4)
},{}],1708:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1707,"polyfill/polyfill-base.js":1711,"utils":1712}],1709:[function(require,module,exports){
module.exports=require(14)
},{}],1710:[function(require,module,exports){
module.exports=require(15)
},{}],1711:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1709,"./lib/window.console.js":1710}],1712:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1713,"./lib/timers.js":1714}],1713:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1707,"polyfill/polyfill-base.js":1717}],1714:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1717}],1715:[function(require,module,exports){
module.exports=require(14)
},{}],1716:[function(require,module,exports){
module.exports=require(15)
},{}],1717:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1715,"./lib/window.console.js":1716}],1718:[function(require,module,exports){
arguments[4][5][0].apply(exports,arguments)
},{"event-dom":1719,"js-ext/extra/classes.js":1814,"js-ext/extra/hashmap.js":1815,"js-ext/lib/object.js":1816,"js-ext/lib/promise.js":1817,"js-ext/lib/string.js":1818,"polyfill":1830,"utils/lib/timers.js":1831,"vdom":1887}],1719:[function(require,module,exports){
module.exports=require(267)
},{"event":1723,"js-ext/extra/hashmap.js":1739,"js-ext/lib/array.js":1740,"js-ext/lib/object.js":1741,"js-ext/lib/string.js":1742,"polyfill/polyfill-base.js":1754,"utils":1755,"vdom":1813}],1720:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":1725,"js-ext/lib/object.js":1726,"polyfill/polyfill-base.js":1738}],1721:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":1720}],1722:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":1720,"js-ext/extra/classes.js":1724,"js-ext/lib/object.js":1726}],1723:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":1720,"./event-emitter.js":1721,"./event-listener.js":1722}],1724:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":1726,"js-ext/extra/hashmap.js":1725,"polyfill/polyfill-base.js":1729}],1725:[function(require,module,exports){
module.exports=require(4)
},{}],1726:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1725,"polyfill/polyfill-base.js":1729,"utils":1730}],1727:[function(require,module,exports){
module.exports=require(14)
},{}],1728:[function(require,module,exports){
module.exports=require(15)
},{}],1729:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1727,"./lib/window.console.js":1728}],1730:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1731,"./lib/timers.js":1732}],1731:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1725,"polyfill/polyfill-base.js":1735}],1732:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1735}],1733:[function(require,module,exports){
module.exports=require(14)
},{}],1734:[function(require,module,exports){
module.exports=require(15)
},{}],1735:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1733,"./lib/window.console.js":1734}],1736:[function(require,module,exports){
module.exports=require(14)
},{}],1737:[function(require,module,exports){
module.exports=require(15)
},{}],1738:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1736,"./lib/window.console.js":1737}],1739:[function(require,module,exports){
module.exports=require(4)
},{}],1740:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":1745}],1741:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1739,"polyfill/polyfill-base.js":1745,"utils":1746}],1742:[function(require,module,exports){
module.exports=require(29)
},{}],1743:[function(require,module,exports){
module.exports=require(14)
},{}],1744:[function(require,module,exports){
module.exports=require(15)
},{}],1745:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1743,"./lib/window.console.js":1744}],1746:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1747,"./lib/timers.js":1748}],1747:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1739,"polyfill/polyfill-base.js":1751}],1748:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1751}],1749:[function(require,module,exports){
module.exports=require(14)
},{}],1750:[function(require,module,exports){
module.exports=require(15)
},{}],1751:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1749,"./lib/window.console.js":1750}],1752:[function(require,module,exports){
module.exports=require(14)
},{}],1753:[function(require,module,exports){
module.exports=require(15)
},{}],1754:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1752,"./lib/window.console.js":1753}],1755:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1756,"./lib/timers.js":1757}],1756:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1739,"polyfill/polyfill-base.js":1760}],1757:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1760}],1758:[function(require,module,exports){
module.exports=require(14)
},{}],1759:[function(require,module,exports){
module.exports=require(15)
},{}],1760:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1758,"./lib/window.console.js":1759}],1761:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],1762:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":1766,"js-ext/extra/hashmap.js":1763,"polyfill/polyfill-base.js":1772}],1763:[function(require,module,exports){
module.exports=require(4)
},{}],1764:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":1765,"../lib/object.js":1766,"./classes.js":1762,"js-ext/extra/hashmap.js":1763,"polyfill/lib/weakmap.js":1770}],1765:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":1772}],1766:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1763,"polyfill/polyfill-base.js":1772,"utils":1773}],1767:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":1772}],1768:[function(require,module,exports){
module.exports=require(29)
},{}],1769:[function(require,module,exports){
module.exports=require(14)
},{}],1770:[function(require,module,exports){
module.exports=require(57)
},{}],1771:[function(require,module,exports){
module.exports=require(15)
},{}],1772:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1769,"./lib/window.console.js":1771}],1773:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1774,"./lib/timers.js":1775}],1774:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1763,"polyfill/polyfill-base.js":1778}],1775:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1778}],1776:[function(require,module,exports){
module.exports=require(14)
},{}],1777:[function(require,module,exports){
module.exports=require(15)
},{}],1778:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1776,"./lib/window.console.js":1777}],1779:[function(require,module,exports){
module.exports=require(66)
},{}],1780:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":1779}],1781:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":1779}],1782:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":1779}],1783:[function(require,module,exports){
module.exports=require(14)
},{}],1784:[function(require,module,exports){
module.exports=require(15)
},{}],1785:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1783,"./lib/window.console.js":1784}],1786:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1787,"./lib/timers.js":1788}],1787:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1763,"polyfill/polyfill-base.js":1791}],1788:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1791}],1789:[function(require,module,exports){
module.exports=require(14)
},{}],1790:[function(require,module,exports){
module.exports=require(15)
},{}],1791:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1789,"./lib/window.console.js":1790}],1792:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":1793}],1793:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":1794,"js-ext/lib/object.js":1795}],1794:[function(require,module,exports){
module.exports=require(4)
},{}],1795:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1794,"polyfill/polyfill-base.js":1798,"utils":1799}],1796:[function(require,module,exports){
module.exports=require(14)
},{}],1797:[function(require,module,exports){
module.exports=require(15)
},{}],1798:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1796,"./lib/window.console.js":1797}],1799:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1800,"./lib/timers.js":1801}],1800:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1794,"polyfill/polyfill-base.js":1804}],1801:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1804}],1802:[function(require,module,exports){
module.exports=require(14)
},{}],1803:[function(require,module,exports){
module.exports=require(15)
},{}],1804:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1802,"./lib/window.console.js":1803}],1805:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":1763,"js-ext/lib/object.js":1766,"js-ext/lib/string.js":1768,"polyfill":1785,"polyfill/extra/transition.js":1780,"polyfill/extra/vendorCSS.js":1782}],1806:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":1763,"js-ext/lib/object.js":1766,"polyfill":1785}],1807:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":1763,"js-ext/lib/object.js":1766,"js-ext/lib/string.js":1768,"polyfill":1785}],1808:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":1761,"./attribute-extractor.js":1805,"./element-array.js":1806,"./html-parser.js":1809,"./node-parser.js":1810,"./vdom-ns.js":1811,"./vnode.js":1812,"js-ext/extra/hashmap.js":1763,"js-ext/lib/object.js":1766,"js-ext/lib/promise.js":1767,"js-ext/lib/string.js":1768,"polyfill":1785,"polyfill/extra/transition.js":1780,"polyfill/extra/transitionend.js":1781,"polyfill/extra/vendorCSS.js":1782,"utils":1786,"window-ext":1792}],1809:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":1805,"./vdom-ns.js":1811,"js-ext/extra/hashmap.js":1763,"js-ext/lib/object.js":1766,"polyfill":1785}],1810:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":1805,"./vdom-ns.js":1811,"./vnode.js":1812,"js-ext/extra/hashmap.js":1763,"js-ext/lib/object.js":1766,"polyfill":1785}],1811:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":1763,"js-ext/lib/object.js":1766,"polyfill":1785}],1812:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":1805,"./html-parser.js":1809,"./vdom-ns.js":1811,"js-ext/extra/hashmap.js":1763,"js-ext/extra/lightmap.js":1764,"js-ext/lib/array.js":1765,"js-ext/lib/object.js":1766,"js-ext/lib/string.js":1768,"polyfill":1785,"utils/lib/timers.js":1788}],1813:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":1807,"./partials/extend-element.js":1808,"./partials/node-parser.js":1810,"js-ext/extra/hashmap.js":1763,"js-ext/lib/object.js":1766,"utils/lib/timers.js":1788}],1814:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":1816,"js-ext/extra/hashmap.js":1815,"polyfill/polyfill-base.js":1821}],1815:[function(require,module,exports){
module.exports=require(4)
},{}],1816:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1815,"polyfill/polyfill-base.js":1821,"utils":1822}],1817:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":1821}],1818:[function(require,module,exports){
module.exports=require(29)
},{}],1819:[function(require,module,exports){
module.exports=require(14)
},{}],1820:[function(require,module,exports){
module.exports=require(15)
},{}],1821:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1819,"./lib/window.console.js":1820}],1822:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1823,"./lib/timers.js":1824}],1823:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1815,"polyfill/polyfill-base.js":1827}],1824:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1827}],1825:[function(require,module,exports){
module.exports=require(14)
},{}],1826:[function(require,module,exports){
module.exports=require(15)
},{}],1827:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1825,"./lib/window.console.js":1826}],1828:[function(require,module,exports){
module.exports=require(14)
},{}],1829:[function(require,module,exports){
module.exports=require(15)
},{}],1830:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1828,"./lib/window.console.js":1829}],1831:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1834}],1832:[function(require,module,exports){
module.exports=require(14)
},{}],1833:[function(require,module,exports){
module.exports=require(15)
},{}],1834:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1832,"./lib/window.console.js":1833}],1835:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],1836:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":1840,"js-ext/extra/hashmap.js":1837,"polyfill/polyfill-base.js":1846}],1837:[function(require,module,exports){
module.exports=require(4)
},{}],1838:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":1839,"../lib/object.js":1840,"./classes.js":1836,"js-ext/extra/hashmap.js":1837,"polyfill/lib/weakmap.js":1844}],1839:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":1846}],1840:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1837,"polyfill/polyfill-base.js":1846,"utils":1847}],1841:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":1846}],1842:[function(require,module,exports){
module.exports=require(29)
},{}],1843:[function(require,module,exports){
module.exports=require(14)
},{}],1844:[function(require,module,exports){
module.exports=require(57)
},{}],1845:[function(require,module,exports){
module.exports=require(15)
},{}],1846:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1843,"./lib/window.console.js":1845}],1847:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1848,"./lib/timers.js":1849}],1848:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1837,"polyfill/polyfill-base.js":1852}],1849:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1852}],1850:[function(require,module,exports){
module.exports=require(14)
},{}],1851:[function(require,module,exports){
module.exports=require(15)
},{}],1852:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1850,"./lib/window.console.js":1851}],1853:[function(require,module,exports){
module.exports=require(66)
},{}],1854:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":1853}],1855:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":1853}],1856:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":1853}],1857:[function(require,module,exports){
module.exports=require(14)
},{}],1858:[function(require,module,exports){
module.exports=require(15)
},{}],1859:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1857,"./lib/window.console.js":1858}],1860:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1861,"./lib/timers.js":1862}],1861:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1837,"polyfill/polyfill-base.js":1865}],1862:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1865}],1863:[function(require,module,exports){
module.exports=require(14)
},{}],1864:[function(require,module,exports){
module.exports=require(15)
},{}],1865:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1863,"./lib/window.console.js":1864}],1866:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":1867}],1867:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":1868,"js-ext/lib/object.js":1869}],1868:[function(require,module,exports){
module.exports=require(4)
},{}],1869:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1868,"polyfill/polyfill-base.js":1872,"utils":1873}],1870:[function(require,module,exports){
module.exports=require(14)
},{}],1871:[function(require,module,exports){
module.exports=require(15)
},{}],1872:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1870,"./lib/window.console.js":1871}],1873:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1874,"./lib/timers.js":1875}],1874:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1868,"polyfill/polyfill-base.js":1878}],1875:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1878}],1876:[function(require,module,exports){
module.exports=require(14)
},{}],1877:[function(require,module,exports){
module.exports=require(15)
},{}],1878:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1876,"./lib/window.console.js":1877}],1879:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":1837,"js-ext/lib/object.js":1840,"js-ext/lib/string.js":1842,"polyfill":1859,"polyfill/extra/transition.js":1854,"polyfill/extra/vendorCSS.js":1856}],1880:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":1837,"js-ext/lib/object.js":1840,"polyfill":1859}],1881:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":1837,"js-ext/lib/object.js":1840,"js-ext/lib/string.js":1842,"polyfill":1859}],1882:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":1835,"./attribute-extractor.js":1879,"./element-array.js":1880,"./html-parser.js":1883,"./node-parser.js":1884,"./vdom-ns.js":1885,"./vnode.js":1886,"js-ext/extra/hashmap.js":1837,"js-ext/lib/object.js":1840,"js-ext/lib/promise.js":1841,"js-ext/lib/string.js":1842,"polyfill":1859,"polyfill/extra/transition.js":1854,"polyfill/extra/transitionend.js":1855,"polyfill/extra/vendorCSS.js":1856,"utils":1860,"window-ext":1866}],1883:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":1879,"./vdom-ns.js":1885,"js-ext/extra/hashmap.js":1837,"js-ext/lib/object.js":1840,"polyfill":1859}],1884:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":1879,"./vdom-ns.js":1885,"./vnode.js":1886,"js-ext/extra/hashmap.js":1837,"js-ext/lib/object.js":1840,"polyfill":1859}],1885:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":1837,"js-ext/lib/object.js":1840,"polyfill":1859}],1886:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":1879,"./html-parser.js":1883,"./vdom-ns.js":1885,"js-ext/extra/hashmap.js":1837,"js-ext/extra/lightmap.js":1838,"js-ext/lib/array.js":1839,"js-ext/lib/object.js":1840,"js-ext/lib/string.js":1842,"polyfill":1859,"utils/lib/timers.js":1862}],1887:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":1881,"./partials/extend-element.js":1882,"./partials/node-parser.js":1884,"js-ext/extra/hashmap.js":1837,"js-ext/lib/object.js":1840,"utils/lib/timers.js":1862}],1888:[function(require,module,exports){
module.exports=require(14)
},{}],1889:[function(require,module,exports){
module.exports=require(15)
},{}],1890:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1888,"./lib/window.console.js":1889}],1891:[function(require,module,exports){
module.exports=require(4)
},{}],1892:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1891,"polyfill/polyfill-base.js":1896,"utils":1897}],1893:[function(require,module,exports){
module.exports=require(29)
},{}],1894:[function(require,module,exports){
module.exports=require(14)
},{}],1895:[function(require,module,exports){
module.exports=require(15)
},{}],1896:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1894,"./lib/window.console.js":1895}],1897:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1898,"./lib/timers.js":1899}],1898:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1891,"polyfill/polyfill-base.js":1902}],1899:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1902}],1900:[function(require,module,exports){
module.exports=require(14)
},{}],1901:[function(require,module,exports){
module.exports=require(15)
},{}],1902:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1900,"./lib/window.console.js":1901}],1903:[function(require,module,exports){
module.exports=require(14)
},{}],1904:[function(require,module,exports){
module.exports=require(15)
},{}],1905:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1903,"./lib/window.console.js":1904}],1906:[function(require,module,exports){
module.exports=require(567)
},{"js-ext/extra/hashmap.js":1891,"js-ext/lib/object.js":1892,"js-ext/lib/string.js":1893,"polyfill":1905}],1907:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1908,"./lib/timers.js":1909}],1908:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1707,"polyfill/polyfill-base.js":1912}],1909:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1912}],1910:[function(require,module,exports){
module.exports=require(14)
},{}],1911:[function(require,module,exports){
module.exports=require(15)
},{}],1912:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1910,"./lib/window.console.js":1911}],1913:[function(require,module,exports){
"use strict";
require('js-ext/lib/object.js');
require('polyfill');
require('./css/scrollable.css');
/**
*
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module focusmanager
* @class FocusManager
* @since 0.0.1
*/
var NAME = '[scrollable]: ',
POSITION = 'position',
OVERFLOW = 'overflow',
SYNC_TIMER = 1000,
SCROLL_HANDLE = '<span plugin-dd="true" plugin-constrain="true" constrain-selector="span"></span>',
VERTICAL_CONT = '<span class="itsa-vscroll-cont">'+SCROLL_HANDLE+'</span>',
HORIZONTAL_CONT = '<span class="itsa-hscroll-cont">'+SCROLL_HANDLE+'</span>',
createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
var DOCUMENT = window.document,
later = require('utils').later,
Scrollable, Event, setupEvents, DD, isSafari;
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
/*jshint boss:true */
if (Scrollable=window._ITSAmodules.Scrollable) {
/*jshint boss:false */
return Scrollable; // Scrollable was already created
}
require('window-ext')(window);
require('node-plugin')(window);
Event = require('event-mobile')(window);
DD = require('drag')(window);
DD.init(); // ITSA combines the Drag-module with drag-drop into ITSA.DD
isSafari = require('useragent')(window).isSafari;
setupEvents = function() {
Event.after('UI:dd-drag', function(e) {
var dragNode = e.target,
dragContainer = dragNode.getParent(),
top = dragNode.top - dragContainer.top,
height = dragNode.height,
heightContainer = dragContainer.height,
effectiveRegion = heightContainer - height,
percentedMoved = top/effectiveRegion,
host = dragContainer.getParent(),
hostScrollHeight = host.scrollHeight,
hostHeight = host.height,
model = host._plugin.scroll.model;
model.top = Math.round(percentedMoved*(hostScrollHeight-hostHeight));
}, '[plugin-scroll="true"] >span.itsa-vscroll-cont span');
Event.after('UI:dd-drag', function(e) {
var dragNode = e.target,
dragContainer = dragNode.getParent(),
left = parseInt(dragNode.getInlineStyle('left'), 10),
width = dragNode.width,
widthContainer = dragContainer.width,
effectiveRegion = widthContainer - width,
percentedMoved = left/effectiveRegion,
host = dragContainer.getParent(),
hostScrollWidth = host.scrollWidth,
hostWidth = host.width,
model = host._plugin.scroll.model;
model.left = Math.round(percentedMoved*(hostScrollWidth-hostWidth));
}, '[plugin-scroll="true"] >span.itsa-hscroll-cont span');
Event.after('UI:nodecontentchange', function(e) {
var node = e.target;
node._plugin && node._plugin.scroll && node._plugin.scroll.sync();
}, '[plugin-scroll="true"][scroll-ready="true"]');
};
setupEvents();
window._ITSAmodules.Scrollable = Scrollable = DOCUMENT.definePlugin('scroll',
function() {
// initializer
// because we cannot predict if the size of the container are goiig to change (fe by ancestor-classes)
// we need to sync by timer as well:
var instance = this;
instance._syncTimer = later(instance.sync.bind(instance), SYNC_TIMER);
}, {
attrs: {
x: 'boolean',
y: 'boolean',
left: 'number',
top: 'number',
light: 'boolean',
autohide: 'boolean',
disabled: 'boolean'
},
defaults: {
x: true,
y: true,
left: 0,
top: 0,
},
render: function() {
var instance = this,
host = instance.host,
inlinePosition, globalPosition;
// default position to relative: check first inlinestyle because this goes quicker
instance._inlinePosition = inlinePosition = host.getInlineStyle(POSITION);
inlinePosition || (globalPosition=host.getStyle(POSITION));
if ((inlinePosition==='static') || (inlinePosition==='fixed') || (globalPosition==='static') || (globalPosition==='fixed')) {
inlinePosition = 'relative';
host.setInlineStyle(POSITION, inlinePosition);
}
instance._inlineOverflow = host.getInlineStyle(OVERFLOW);
instance._inlineOverflow && host.removeInlineStyle(OVERFLOW);
host.addSystemElement(VERTICAL_CONT, false, false); // don't want silent insert --> need to render the plugins
host.addSystemElement(HORIZONTAL_CONT, false, false);
},
sync: function() {
var instance = this,
host = instance.host,
model = instance.model,
scrollHeight = host.scrollHeight,
scrollWidth = host.scrollWidth,
height = host.height,
width = host.width,
scrollLeft = model.left,
scrollTop = model.top,
vscroller = host.getElement('span.itsa-vscroll-cont', true),
hscroller = host.getElement('span.itsa-hscroll-cont', true),
sizeHandle, effectiveRegion, maxScrollAmount, scrollAmount, handleNode,
vScrollerVisible, hScrollerVisible;
// safari showed it miscalculates scrollWidth (perhaps also scrollHeight)
// in certain circumstances by returning 1px too much
// this may lead into a scroller when it shouldn;t be there:
if (isSafari) {
(scrollHeight===(height+1)) && (scrollHeight=height);
(scrollWidth===(width+1)) && (scrollWidth=width);
}
vScrollerVisible = (scrollHeight>height),
hScrollerVisible = (scrollWidth>width),
vscroller.toggleClass('itsa-visible', vScrollerVisible);
hscroller.toggleClass('itsa-visible', hScrollerVisible);
host.scrollTop = scrollTop;
host.scrollLeft = scrollLeft;
// because scrollTop and scrollLeft have their restrictions, we need to reset them into the model (in case a corrcetion has taken place)
model.top = scrollTop = host.scrollTop;
model.left = scrollLeft = host.scrollLeft;
if (vScrollerVisible) {
handleNode = vscroller.getElement('span');
if (!handleNode.hasClass('dd-dragging')) {
sizeHandle = Math.round(height*(height/scrollHeight));
effectiveRegion = height - sizeHandle;
maxScrollAmount = scrollHeight - height;
scrollAmount = effectiveRegion * Math.max(0, Math.min(1, (scrollTop/maxScrollAmount)));
handleNode.setInlineStyle('top', scrollAmount+'px')
.setInlineStyle('height', sizeHandle+'px');
}
vscroller.setInlineStyle('top', scrollTop+'px')
.setInlineStyle('right', -scrollLeft+'px');
}
if (hScrollerVisible) {
handleNode = hscroller.getElement('span');
if (!handleNode.hasClass('dd-dragging')) {
sizeHandle = Math.round(width*(width/scrollWidth));
effectiveRegion = width - sizeHandle;
maxScrollAmount = scrollWidth - width;
scrollAmount = effectiveRegion * Math.max(0, Math.min(1, (scrollLeft/maxScrollAmount)));
handleNode.setInlineStyle('left', scrollAmount+'px')
.setInlineStyle('width', sizeHandle+'px');
}
hscroller.setInlineStyle('bottom', -scrollTop+'px')
.setInlineStyle('left', scrollLeft+'px');
}
},
destroy: function() {
var instance = this,
host = instance.host,
inlinePosition, inlineOverflow;
instance._syncTimer.cancel();
/*jshint boss:true */
if (inlinePosition=instance._inlinePosition) {
/*jshint boss:false */
host.setInlineStyle(POSITION, inlinePosition);
}
else {
host.removeInlineStyle(POSITION);
}
/*jshint boss:true */
if (inlineOverflow=instance._inlineOverflow) {
/*jshint boss:false */
host.setInlineStyle(OVERFLOW, inlineOverflow);
}
else {
host.removeInlineStyle(OVERFLOW);
}
}
});
return Scrollable;
};
},{"./css/scrollable.css":1234,"drag":1236,"event-mobile":1604,"js-ext/extra/hashmap.js":1707,"js-ext/lib/object.js":1708,"node-plugin":1718,"polyfill":1890,"useragent":1906,"utils":1907,"window-ext":1914}],1914:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":1915}],1915:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":1916,"js-ext/lib/object.js":1917}],1916:[function(require,module,exports){
module.exports=require(4)
},{}],1917:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1916,"polyfill/polyfill-base.js":1920,"utils":1921}],1918:[function(require,module,exports){
module.exports=require(14)
},{}],1919:[function(require,module,exports){
module.exports=require(15)
},{}],1920:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1918,"./lib/window.console.js":1919}],1921:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1922,"./lib/timers.js":1923}],1922:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1916,"polyfill/polyfill-base.js":1926}],1923:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1926}],1924:[function(require,module,exports){
module.exports=require(14)
},{}],1925:[function(require,module,exports){
module.exports=require(15)
},{}],1926:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1924,"./lib/window.console.js":1925}],1927:[function(require,module,exports){
"use strict";
/**
* Creating floating Panel-nodes which can be shown and hidden.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
*
* @module panel
* @class Panel
* @since 0.0.1
*/
require('js-ext/lib/object.js');
require('js-ext/lib/string.js');
require('polyfill');
require('./css/panel.css');
var NAME = '[panel]: ',
PANEL_Z = 1000,
MODAL_Z = 2000,
PANEL_TOP = 999,
MAX_WIDTH_FULL_BUTTONS = 150,
createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
var DOCUMENT = window.document,
LightMap = require('js-ext/extra/lightmap.js'),
Panel, Event, setupEvents, DD, stackManager, insertModalLayer;
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
/*jshint boss:true */
if (Panel=window._ITSAmodules.Panel) {
/*jshint boss:false */
return Panel; // Panel was already created
}
require('window-ext')(window);
require('node-plugin')(window);
require('focusmanager')(window);
require('scrollable')(window);
Event = require('event-mobile')(window);
DD = require('drag')(window);
DD.init(); // ITSA combines the Drag-module with drag-drop into ITSA.DD
/*
* Generates a modal-layer.
*
* @method insertModalLayer
* @protected
* @since 0.0.1
*/
insertModalLayer = function() {
DOCUMENT.body.addSystemElement('<div class="itsa-modal-layer itsa-no-display"></div>', null, true);
};
insertModalLayer();
/*
* Sets up all events needed for Panel's to work well.
*
* @method setupEvents
* @protected
* @since 0.0.1
*/
setupEvents = function() {
Event.defineEvent('panel:buttonhide').defaultFn(function(e) {
var model = e.model;
model.visible = false;
(typeof model.callback==='function') && model.callback(e.button);
});
Event.before('panel:buttonhide', function(e) {
var model = e.model;
if (typeof model.validate==='function') {
model.validate(e) || e.preventDefault();
}
});
Event.after('tap', function(e) {
var buttonNode = e.target,
panelNode = buttonNode.inside('[plugin-panel="true"]'),
plugin = panelNode._plugin.panel,
model = plugin.model;
Event.emit(panelNode, 'panel:buttonhide', {button: buttonNode, plugin: plugin, model: model});
}, '[plugin-panel="true"] >div[is="header"] button, [plugin-panel="true"] >div[is="footer"] button, [plugin-panel="true"] >button');
};
setupEvents();
/*
* An object with several method to control stacked panels.
*
* @property stackManager
* @type Object
* @protected
* @since 0.0.1
*/
stackManager = DOCUMENT.stackManager = {
elements: new LightMap(),
isOnTop: function(host) {
return (this.topElement===host);
},
setOnTop: function(host) {
this.topElement = host;
},
registerModal: function(host) {
var instance = this;
instance.elements.set(host, true);
instance.showModalLayer(true);
},
unRegisterModal: function(host) {
var instance = this;
instance.elements.delete(host);
instance.hasModalElements() || instance.showModalLayer(false);
},
hasModalElements: function() {
return (this.elements.size()>0);
},
showModalLayer: function(show) {
var modalLayer = DOCUMENT.getElement('body >div[is="system-node"].itsa-modal-layer', true);
modalLayer.toggleClass('itsa-no-display', !show);
}
};
/*
* Creates a new Panel-node which is prepended to body.
*
* @for document
* @method createPanel
* @param data {Object} the panel plugin's `model`-object
* @param [systemNode] {boolean} whether the created node should be a systemnode
* @return {HTMLElement} the created panelNode
* @since 0.0.1
*/
DOCUMENT.createPanel = function(data, systemNode) {
var panel;
if (!Object.isObject(data)) {
console.warn('document.createPanel should be invoked with an object as first argument.');
return;
}
if (systemNode) {
panel = DOCUMENT.body.addSystemElement('<div></div>');
}
else {
panel = DOCUMENT.body.prepend('<div></div>');
}
panel.plug('panel', null, data);
return panel;
};
window._ITSAmodules.Panel = Panel = DOCUMENT.definePlugin('panel', function() {
var instance = this,
model = instance.model,
host = instance.host,
allDivs, serverHeader, serverContent, serverFooter;
if (host.getAttr('panel-rendered')==='true') {
// serverside rendered --> we might need to catch header, content and footer
// for they aren't set in the attributes
allDivs = host.getAll('>div');
serverHeader = allDivs[0] && allDivs[0].getHTML();
serverContent = allDivs[1] && allDivs[1].getHTML();
serverFooter = allDivs[2] && allDivs[2].getHTML();
serverHeader && instance.defineWhenUndefined('header', serverHeader);
serverContent && instance.defineWhenUndefined('content', serverContent);
if (serverFooter) {
instance.defineWhenUndefined('footer', serverFooter);
}
}
instance._resizeHandler = Event.after('UI:resize', function() {
var isMobileWidth = (window.getWidth()<=480);
model.center && instance.centerPanel();
host[((model.draggable && !isMobileWidth) ? '' : 'un')+'plug']('dd');
instance.setPanelWidth(isMobileWidth);
});
}, {
/*
* All panel's attributes: these attributes will be read during initalization and updated during `sync`
* In the dom, the attributenames are prepended with `pluginName-`. The property-values should be the property-types
* that belong to the property, this way the attributes get right casted into model.
*
* @property attrs
* @default {
* visible: 'boolean',
* onTopWhenShowed: 'boolean',
* headerCloseBtn: 'boolean',
* stack: 'number',
* left: 'number',
* top: 'number',
* center: 'boolean',
* minWidth: 'number',
* maxWidth: 'number',
* minHeight: 'number',
* maxHeight: 'number',
* modal: 'boolean',
* draggable: 'boolean',
* focusmanaged: 'boolean'
* }
* @type Object
* @since 0.0.1
*/
attrs: {
visible: 'boolean',
onTopWhenShowed: 'boolean',
headerCloseBtn: 'boolean',
stack: 'number',
left: 'number',
top: 'number',
center: 'boolean',
minWidth: 'number',
maxWidth: 'number',
minHeight: 'number',
maxHeight: 'number',
modal: 'boolean',
draggable: 'boolean',
focusmanaged: 'boolean'
},
/*
* Any default values for attributes specified by `attrs`.
*
* @property defaults
* @default {
* visible: false,
* onTopWhenShowed: true,
* stack: 1,
* center: true,
* modal: true,
* focusmanaged: true,
* headerCloseBtn: false
* }
* @type Object
* @since 0.0.1
*/
defaults: {
visible: false,
onTopWhenShowed: true,
stack: 1,
center: true,
modal: true,
focusmanaged: true,
headerCloseBtn: false
},
/*
* Renders the plugin. This method is invoked only once: at the end of initialization.
* It should be used to render any nodes inside the host. Not all plugins need this.
*
* @method render
* @since 0.0.1
*/
render: function() {
var instance = this,
host = instance.host,
newContent;
host.setClass('itsa-hidden');
newContent = '<div is="header"></div>'+
'<div is="content"></div>'+
'<div is="footer"></div>' +
'<button class="panel-close pure-button">x</button>';
host.setHTML(newContent);
},
/*
* Syncs plugin.model's data with the host. Not its attributes: they will be synced automaticly.
* Is invoked after every change of plugin.model's data.
*
* @method sync
* @since 0.0.1
*/
sync: function() {
var instance = this,
host = instance.host,
model = instance.model,
header = model.header,
content = model.content,
footer = model.footer,
stack = model.stack,
divs = host.getAll('>div'),
headerNode = divs[0],
contentNode = divs[1],
footerNode = divs[2],
isMobileWidth = (window.getWidth()<=480),
buttonCloseNode = host.getElement('>button'),
showHeaderCloseBtn = model.headerCloseBtn,
zIndex, isOnTop;
(header==='undefined') && (header=undefined);
(footer==='undefined') && (footer=undefined);
if (!footer || !footer.contains('button')) {
showHeaderCloseBtn = true;
}
if (showHeaderCloseBtn && !header) {
header = '';
}
(header!==undefined) && headerNode.setHTML(header || '');
buttonCloseNode.toggleClass('itsa-no-display', !showHeaderCloseBtn);
headerNode.toggleClass('itsa-hidden', (header===undefined));
contentNode.setHTML(content || '');
contentNode.plug('scroll');
(footer!==undefined) && footerNode.setHTML(footer || '');
footerNode.toggleClass('itsa-hidden', (footer===undefined));
zIndex = (model.modal ? MODAL_Z : PANEL_Z);
if (model.visible && model.modal) {
stackManager.registerModal(host);
}
else {
stackManager.unRegisterModal(host);
}
if (model.onTopWhenShowed && model.visible && host.hasClass('itsa-hidden')) {
instance.showOnTop();
}
isOnTop = stackManager.isOnTop(host);
zIndex += isOnTop ? PANEL_TOP : stack;
host.setInlineStyle('z-index', zIndex);
model.minWidth && host.setInlineStyle('minWidth', model.minWidth);
model.minHeight && host.setInlineStyle('minHeight', model.minHeight);
model.maxHeight && host.setInlineStyle('maxHeight', model.maxHeight);
instance.setPanelWidth(isMobileWidth);
if (model.center && (!model.draggable || ((instance._previousVisible!==true) && model.visible))) {
instance.centerPanel();
}
else if (!model.center && !host.hasClass('dd-dragging')) {
host.setInlineStyles([
{property: 'left', value: model.left+'px'},
{property: 'top', value: model.top+'px'}
]);
}
// we can plug/unplug multiple times --> node-plugin won't do anything when there are no changes
host[((model.draggable && !isMobileWidth) ? '' : 'un')+'plug']('dd');
host[(model.focusmanaged ? '' : 'un')+'plug']('fm');
host.toggleClass('itsa-full-draggable', model.draggable && !header);
if (model.draggable && header) {
host.hasAttr('dd-handle') || host.setAttr('dd-handle', '>div:first-child');
}
else {
host.hasAttr('dd-handle') && host.removeAttr('dd-handle');
}
host.toggleClass('itsa-hidden', !model.visible);
// if there is a change of model.visible, the fire an event:
if (instance._previousVisible!==model.visible) {
Event.emit(host, 'panel:'+ (model.visible ? 'shown' : 'hidden'), {plugin: instance, model: model});
instance._previousVisible = model.visible;
}
if (isOnTop && model.modal) {
host.focus();
}
},
/*
* Centeres the panel on the screen.
*
* @method centerPanel
* @since 0.0.1
*/
centerPanel: function() {
var instance = this,
host = instance.host,
model = instance.model;
model.left = Math.round((window.getWidth()-host.width)/2);
model.top = Math.round((window.getHeight()-host.height)/2);
host.setInlineStyles([
{property: 'left', value: model.left+'px'},
{property: 'top', value: model.top+'px'}
]);
},
/*
* Sets the style `maxWidth` and the attribute `expand-buttons`. These need to be set after width-changes of either the panel or the screen.
*
* @method setPanelWidth
* @param isMobileWidth {Boolean} whether the current screen-width is `mobile-width`
* @since 0.0.1
*/
setPanelWidth: function(isMobileWidth) {
var instance = this,
host = instance.host,
model = instance.model;
if (isMobileWidth) {
host.hasInlineStyle('maxWidth') && host.removeInlineStyle('maxWidth');
host.setAttr('expand-buttons', 'true');
}
else {
if (model.maxWidth) {
host.setInlineStyle('maxWidth', model.maxWidth);
if (host.width>MAX_WIDTH_FULL_BUTTONS) {
host.removeAttr('expand-buttons');
}
else {
host.setAttr('expand-buttons', 'true');
}
}
else {
host.removeAttr('expand-buttons');
}
}
},
/*
* Maes this panel as the `top`-panel of the stack.
*
* @method showOnTop
* @since 0.0.1
*/
showOnTop: function() {
stackManager.setOnTop(this.host);
},
/*
* Cleansup the plugin. Is invoked whenever a plugin gets unplugged or its host gets removed from the dom.
*
* @method destroy
* @since 0.0.1
*/
destroy: function() {
var instance = this,
host = instance.host;
instance._resizeHandler.detach();
host.removeInlineStyle('z-index');
host.unplug('dd');
host.unplug('fm');
host.removeClass(['itsa-hidden', 'itsa-full-draggable']);
stackManager.unRegisterModal(host);
host.empty();
}
});
return Panel;
};
},{"./css/panel.css":264,"drag":266,"event-mobile":634,"focusmanager":738,"js-ext/extra/hashmap.js":1046,"js-ext/extra/lightmap.js":1047,"js-ext/lib/object.js":1049,"js-ext/lib/string.js":1050,"node-plugin":1061,"polyfill":1233,"scrollable":1913,"window-ext":1914}],1928:[function(require,module,exports){
module.exports=require(14)
},{}],1929:[function(require,module,exports){
module.exports=require(15)
},{}],1930:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1928,"./lib/window.console.js":1929}],1931:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1932,"./lib/timers.js":1933}],1932:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":201,"polyfill/polyfill-base.js":1936}],1933:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1936}],1934:[function(require,module,exports){
module.exports=require(14)
},{}],1935:[function(require,module,exports){
module.exports=require(15)
},{}],1936:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1934,"./lib/window.console.js":1935}],1937:[function(require,module,exports){
var css = "[dropzone] {\n position: relative; /* otherwise we cannot place absolute positioned items */\n}"; (require("/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify"))(css); module.exports = css;
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],1938:[function(require,module,exports){
"use strict";
/**
* Provides `drag and drop` functionality with dropzones
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @example
* DD = require('drag-drop')(window);
* DD.init();
*
* @module drag-drop
* @class DD
* @since 0.0.4
*/
// Redefine the API for the events `dd`, `dd-drag` and `dd-drop`, for they have more properties:
/**
* Emitted during the drag-cycle of a draggable Element (while it is dragged).
*
* @event *:dd-drag (extended by drag-drop)
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.dragNode {HtmlElement} The HtmlElement that is being dragged (equals e.target)
* @param [e.sourceNode] {HtmlElement} The original Element. Only when a `copy` is made --> e.dragNode is being moved while
* e.sourceNode stand at its place.
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.sourceTarget {HtmlElement} the deepest HtmlElement where the mouse lies upon
* @param [e.dropTarget] {HtmlElement} The dropzone HtmlElement that will be available whenever the draggable gets over a dropzone.
* @param e.dd {Promise} Promise that gets fulfilled when dragging is ended. The fullfilled-callback has no arguments.
* @param [e.dropzone] {Promise} a Promise that will be available whenever the draggable gets over a dropzone.
* The Promise that gets fulfilled as soon as the draggable is dropped, or outside the dropzone
* Will fulfill with one argument: `onDropzone` {Boolean} when `true`, the draggable is dropped inside the dropzone, otherwise
* the draggable got outside the dropzone without beging dropped.
* @param e.ctrlKey {Boolean} Whether the Ctrl/cmd key is pressed
* @param e.isCopied {Boolean} Whether the drag is a copy-drag
* @param e.xMouse {Number} the current x-position in the window-view
* @param e.yMouse {Number} the current y-position in the window-view
* @param e.clientX {Number} the current x-position in the window-view
* @param e.clientY {Number} the current y-position in the window-view
* @param e.xMouseOrigin {Number} the original x-position in the document when drag started (incl. scrollOffset)
* @param e.yMouseOrigin {Number} the original y-position in the document when drag started (incl. scrollOffset)
* @param [e.relatives] {NodeList} an optional list that the user could set in a `before`-subscriber of the `dd`-event
* to inform which nodes are related to the draggable node and should be dragged as well.
* @param [e.relativeDragNodes] {NodeList} an optional list that holds the HtmlElements that corresponds with
* the `e.relative` list, but is a list with draggable Elements.
* @since 0.0.1
*/
/**
* Emitted when drag-cycle of a draggable Element is ended.
*
* @event *:dd-drop (extended by drag-drop)
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.dragNode {HtmlElement} The HtmlElement that is being dragged (equals e.target)
* @param [e.sourceNode] {HtmlElement} The original Element. Only when a `copy` is made --> e.dragNode is being moved while
* e.sourceNode stand at its place.
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.sourceTarget {HtmlElement} the deepest HtmlElement where the mouse lies upon
* @param [e.dropTarget] {HtmlElement} The dropzone HtmlElement that will be available whenever the draggable gets over a dropzone.
* @param e.dd {Promise} Promise that gets fulfilled when dragging is ended. The fullfilled-callback has no arguments.
* @param [e.dropzone] {Promise} a Promise that will be available whenever the draggable gets over a dropzone.
* The Promise that gets fulfilled as soon as the draggable is dropped, or outside the dropzone
* Will fulfill with one argument: `onDropzone` {Boolean} when `true`, the draggable is dropped inside the dropzone, otherwise
* the draggable got outside the dropzone without beging dropped.
* @param e.ctrlKey {Boolean} Whether the Ctrl/cmd key is pressed
* @param e.isCopied {Boolean} Whether the drag is a copy-drag
* @param e.xMouse {Number} the current x-position in the window-view
* @param e.yMouse {Number} the current y-position in the window-view
* @param e.clientX {Number} the current x-position in the window-view
* @param e.clientY {Number} the current y-position in the window-view
* @param e.xMouseOrigin {Number} the original x-position in the document when drag started (incl. scrollOffset)
* @param e.yMouseOrigin {Number} the original y-position in the document when drag started (incl. scrollOffset)
* @param [e.relatives] {NodeList} an optional list that the user could set in a `before`-subscriber of the `dd`-event
* to inform which nodes are related to the draggable node and should be dragged as well.
* @param [e.relativeDragNodes] {NodeList} an optional list that holds the HtmlElements that corresponds with
* the `e.relative` list, but is a list with draggable Elements.
* @since 0.0.1
*/
/**
* Emitted when a draggable Element's drag-cycle starts. You can use a `before`-subscriber to specify
* e.relatives, which should be a nodelist with HtmlElements, that should be dragged togehter with the master
* draggable Element.
*
* @event dd (extended by drag-drop)
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.dragNode {HtmlElement} The HtmlElement that is being dragged (equals e.target)
* @param [e.sourceNode] {HtmlElement} The original Element. Only when a `copy` is made --> e.dragNode is being moved while
* e.sourceNode stand at its place.
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.sourceTarget {HtmlElement} the deepest HtmlElement where the mouse lies upon
* @param e.dd {Promise} Promise that gets fulfilled when dragging is ended. The fullfilled-callback has no arguments.
* @param e.ctrlKey {Boolean} Whether the Ctrl/cmd key is pressed
* @param e.isCopied {Boolean} Whether the drag is a copy-drag
* @param e.xMouse {Number} the current x-position in the window-view
* @param e.yMouse {Number} the current y-position in the window-view
* @param e.clientX {Number} the current x-position in the window-view
* @param e.clientY {Number} the current y-position in the window-view
* @param e.xMouseOrigin {Number} the original x-position in the document when drag started (incl. scrollOffset)
* @param e.yMouseOrigin {Number} the original y-position in the document when drag started (incl. scrollOffset)
* @param [e.relatives] {NodeList} an optional list that the user could set in a `before`-subscriber of the `dd`-event
* to inform which nodes are related to the draggable node and should be dragged as well.
* @param [e.relativeDragNodes] {NodeList} an optional list that holds the HtmlElements that corresponds with
* the `e.relative` list, but is a list with draggable Elements.
* @since 0.0.1
*/
/**
* Objecthash containing all specific information about the particular drag-cycle.
* It has a structure like this:
*
* ddProps = {
* sourceNode {HtmlElement} original node (defined by drag-drop)
* dragNode {HtmlElement} Element that is dragged
* x {Number} absolute x-position of the draggable inside `document` when the drag starts
* y {Number} absolute y-position of the draggable inside `document` when the drag starts
* inlineLeft {String} inline css of the property `left` when drag starts
* inlineTop {String} inline css of the property `top` when drag starts
* winConstrained {Boolean} whether the draggable should be constrained to `window`
* xMouseLast {Number} absolute x-position of the mouse inside `document` when the drag starts
* yMouseLast {Number} absolute y-position of the draggable inside `document` when the drag starts
* winScrollLeft {Number} the left-scroll of window when drag starts
* winScrollTop {Number} the top-scroll of window when drag starts
* constrain = { // constrain-properties when constrained to a HtmlElement
* xOrig {Number} x-position in the document, included with left-border-width
* yOrig {Number} y-position in the document, included with top-border-width
* x {Number} xOrig corrected with scroll-left of the constrained node
* y {Number} yOrig corrected with scroll-top of the constrained node
* w {Number} scrollWidth
* h {Number} scrollHeight
* };
* dropzoneSpecified {Boolean} whether the draggable has a dropzone specified (either by `dd-dropzone` or by `dd-emitter`) (defined by drag-drop)
* dragOverEv {Object} Eventhandler that watches for `mousemove` to detect dropzone-over events (defined by drag-drop)
* relatives[{ // Array with objects that represent all draggables that come along with the master-draggable (in case of multiple items), excluded the master draggable itself
* sourceNode {HtmlElement} original node (defined by drag-drop)
* dragNode {HtmlElement} draggable node
* shiftX {Number} the amount of left-pixels that this HtmlElement differs from the dragged element
* shiftY {Number} the amount of top-pixels that this HtmlElement differs from the dragged element
* inlineLeft {String} inline css of the property `left` when drag starts
* inlineTop {String} inline css of the property `top` when drag starts
* }]
* relativeDragNodes [HtmlElements] Array with all `copyied` Nodes corresponding to `ddProps.relatives`. Only in case of copying multiple items (defined by drag-drop)
* }
*
* @property ddProps (extended by drag-drop)
* @default {}
* @type Object
* @since 0.0.1
*/
var DRAG = 'drag',
DROP = 'drop',
NAME = '['+DRAG+'-'+DROP+']: ',
createHashMap = require('js-ext/extra/hashmap.js').createMap,
COPY = 'copy',
DROPZONE = DROP+'zone',
SOURCE = 'source',
DRAGGABLE = DRAG+'gable',
DEL_DRAGGABLE = 'del-'+DRAGGABLE,
DD_MINUS = 'dd-',
DZ_MINUS = 'dz-',
PLUGIN_DD = 'plugin-dd',
DZ_DROPZONE = DZ_MINUS+DROPZONE,
DD_DRAGGING_CLASS = DD_MINUS+DRAG+'ging',
DD_MASTER_CLASS = DD_MINUS+'master',
DD_HANDLE = DD_MINUS+'handle',
DD_SOURCE_ISCOPIED_CLASS = DD_MINUS+COPY+SOURCE,
DD_COPIED_CLASS = DD_MINUS+COPY,
DROPZONE_MOVABLE = DROPZONE+'-movable',
DD_DROPZONE_MOVABLE = DD_MINUS+DROPZONE_MOVABLE,
CONSTRAIN_ATTR = 'constrain-selector',
MOUSE = 'mouse',
DROPZONE_OVER = DROPZONE+'-over',
DROPZONE_DROP = DROPZONE+'-'+DROP,
DD_DROPZONE = DD_MINUS+DROPZONE,
NO_TRANS_CLASS = 'el-notrans', // delivered by `vdom`
DD_HIDDEN_SOURCE_CLASS = DD_MINUS+'hidden-'+SOURCE,
INVISIBLE_CLASS = 'el-invisible', // delivered by `vdom`
DD_TRANSITION_CLASS = DD_MINUS+'transition',
DD_OPACITY_CLASS = DD_MINUS+'opacity',
HIGH_Z_CLASS = DD_MINUS+'high-z',
DD_DROPACTIVE_CLASS = DROPZONE+'-awake',
DD_ABOVE_DROPZONE_CLASS = DD_MINUS+'above'+DROPZONE,
REGEXP_MOVE = /\bmove\b/i,
REGEXP_COPY = /\bcopy\b/i,
REGEXP_ALL = /\b(all|true)\b/i,
EMITTER = 'emitter',
REGEXP_EMITTER = /\bemitter=((\w|,)+)\b/,
DD_EMITTER = DD_MINUS+EMITTER,
MOVE = 'move',
DROPZONE_OUT = DROPZONE+'-out',
DD_DROP = DD_MINUS+DROP,
DD_FAKE = DD_MINUS+'fake-',
DOWN = 'down',
UP = 'up',
KEY = 'key',
MOUSEMOVE = MOUSE+MOVE,
PANMOVE = 'pan'+MOVE,
DD_FAKE_MOUSEMOVE = DD_FAKE+MOUSEMOVE,
UI = 'UI',
DROPZONE_BRACKETS = '[' + DZ_DROPZONE + ']',
EFFECT_ALLOWED = 'effect-allowed',
DD_EFFECT_ALLOWED = DD_MINUS+EFFECT_ALLOWED,
BORDER = 'border',
WIDTH = 'width',
BORDER_LEFT_WIDTH = BORDER+'-left-'+WIDTH,
BORDER_RIGHT_WIDTH = BORDER+'-right-'+WIDTH,
BORDER_TOP_WIDTH = BORDER+'-top-'+WIDTH,
BORDER_BOTTOM_WIDTH = BORDER+'-bottom-'+WIDTH,
LEFT = 'left',
TOP = 'top',
POSITION = 'position',
ABSOLUTE = 'absolute',
TRUE = 'true',
DD_MINUSDRAGGABLE = DD_MINUS+DRAGGABLE,
PLUGIN_ATTRS = [PLUGIN_DD, DD_DROPZONE, CONSTRAIN_ATTR, DD_EMITTER, DD_HANDLE, DD_EFFECT_ALLOWED, DD_DROPZONE_MOVABLE];
require('polyfill/polyfill-base.js');
require('js-ext');
require('./css/drag-drop.css');
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.DragDrop) {
return window._ITSAmodules.DragDrop; // DragDrop was already created
}
var Event = require('event-dom')(window),
DragModule = require('drag')(window),
$superInit = DragModule.init,
ctrlPressed = false,
dropEffect = MOVE,
DOCUMENT = window.document,
isMobile = require('useragent')(window).isMobile,
supportHammer = !!Event.Hammer,
mobileEvents = supportHammer && isMobile,
DD;
require('vdom')(window);
require('node-plugin')(window);
require('window-ext')(window);
DD = {
/**
* Returns the allowed effects on the dragable-HtmlElement. Is determined by the attribute `dd-effect-allowed`
* Will be set to "move" when undefined.
*
* @method _allowedEffects
* @param dragableElement {HtmlElement} HtmlElement that is checked for its allowed effects
* @return {String} allowed effects: "move", "copy" or "all"
* @private
* @since 0.0.1
*/
_allowedEffects: function(dragableElement) {
console.log(NAME, '_allowedEffects');
var allowedEffects = dragableElement.getAttr(DD_EFFECT_ALLOWED);
return allowedEffects || MOVE;
},
/**
* Default function for the `*:dd-drop`-event. Overrides the definition of the `drag`-module.
*
* @method _defFnDrop (extended by drag-drop)
* @param e {Object} eventobject
* @param sourceNode {HtmlElement} the original HtmlElement
* @param dragNode {HtmlElement} the dragged HtmlElement (either original or clone)
* @param dropzoneSpecified {Boolean} whether the sourceNode had a dropzone specified
* @param relatives {Array} hash with all draggables that are being move togerther with the master draggable
* @private
* @since 0.0.1
*/
_defFnDrop: function(e, ddProps) {
console.log(NAME, '_defFnDrop: default function dd-drop. dropzoneSpecified: '+ddProps.dropzoneSpecified);
var instance = this,
sourceNode = ddProps.sourceNode,
dragNode = ddProps.dragNode,
dropzoneSpecified = ddProps.dropzoneSpecified,
relatives = ddProps.relatives,
willBeCopied,
removeClasses = function (node) {
node.removeClass([NO_TRANS_CLASS, HIGH_Z_CLASS, DD_DRAGGING_CLASS, DEL_DRAGGABLE, DD_MASTER_CLASS, DD_SOURCE_ISCOPIED_CLASS]);
};
willBeCopied = (e.dropTarget && ((ctrlPressed && instance.allowCopy(dragNode)) || instance.onlyCopy(dragNode)));
willBeCopied || (e.relativeDragNodes=null);
e.isCopied = willBeCopied;
// handle drop
if (dropzoneSpecified) {
instance._handleDrop(e, sourceNode, dragNode, relatives);
}
else {
PLUGIN_ATTRS.forEach(function(attribute) {
var data = '_del_'+attribute;
if (dragNode.getData(data)) {
dragNode.getPlugin('dd').then(function(plugin) {
delete plugin.model[attribute];
});
dragNode.removeData(data);
}
});
removeClasses(dragNode);
ddProps.relatives && ddProps.relatives.forEach(
function(item) {
removeClasses(item.dragNode);
}
);
}
instance.restoreDraggables = function() {/* NOOP */ return this;};
},
/**
* Default function for the `*:dropzone`-event
*
* @method _defFnOver
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_defFnOver: function(e) {
console.log(NAME, '_defFnOver: default function dropzone');
var dropzone = e.target;
dropzone.setClass(DD_DROPACTIVE_CLASS);
e.sourceNode.setClass(DD_ABOVE_DROPZONE_CLASS);
e.dragNode.setClass(DD_ABOVE_DROPZONE_CLASS);
e.dropzone.then(
function(insideDropTarget) {
dropzone.removeClass(DD_DROPACTIVE_CLASS);
e.sourceNode.removeClass(DD_ABOVE_DROPZONE_CLASS);
e.dragNode.removeClass(DD_ABOVE_DROPZONE_CLASS);
/**
* Emitted when the draggable gets out of the dropzone.
*
* @event *:dropzone-out
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the dropzone
* @param e.dragNode {HtmlElement} The HtmlElement that is being dragged
* @param e.dropzone {Promise} The Promise that gets fulfilled as soon as the draggable is dropped, or outside the dropzone
* Will fulfill with one argument: `onDropzone` {Boolean} when `true`, the draggable is dropped inside the dropzone, otherwise
* the draggable got outside the dropzone without beging dropped.
* @param e.dropTarget {HtmlElement} The dropzone HtmlElement. Equals e.target
* @param e.ctrlKey {Boolean} Whether the Ctrl/cmd key is pressed
* @param e.isCopied {Boolean} Whether the drag is a copy-drag
* @param [e.sourceNode] {HtmlElement} The original Element. Only when a `copy` is made --> e.dragNode is being moved while
* e.sourceNode stand at its place.
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating the draggable
* @param e.sourceTarget {HtmlElement} the deepest HtmlElement of the draggable where the mouse lies upon
* @param e.dd {Promise} Promise that gets fulfilled when dragging is ended. The fullfilled-callback has no arguments.
* @param e.xMouse {Number} the current x-position in the window-view
* @param e.yMouse {Number} the current y-position in the window-view
* @param e.clientX {Number} the current x-position in the window-view
* @param e.clientY {Number} the current y-position in the window-view
* @param e.xMouseOrigin {Number} the original x-position in the document when drag started (incl. scrollOffset)
* @param e.yMouseOrigin {Number} the original y-position in the document when drag started (incl. scrollOffset)
* @param [e.relatives] {NodeList} an optional list that the user could set in a `before`-subscriber of the `dd`-event
* to inform which nodes are related to the draggable node and should be dragged as well.
* @param [e.relativeDragNodes] {NodeList} an optional list that holds the HtmlElements that corresponds with
* the `e.relative` list, but is a list with draggable Elements.
* @since 0.1
*/
insideDropTarget || e._noDDoutEvt || Event.emit(dropzone, e.emitter+':'+DROPZONE_OUT, e);
}
);
},
/**
* Defines the definition of the `dd-drop` event: the last phase of the drag-eventcycle (dd-start, *:dd-drag, *:dd-drop)
*
* @method _defineDropEv
* @param e {Object} eventobject
* @param sourceNode {HtmlElement} the original HtmlElement
* @param dragNode {HtmlElement} the dragged HtmlElement (either original or clone)
* @param dropzoneSpecified {Boolean} whether the sourceNode had a dropzone specified
* @param x {Number} x-position in coordinaties relative to `document` (like getX())
* @param y {Number} y-position in coordinaties relative to `document` (like getX())
* @param inlineLeft {String} inline css `left` for the original sourceNode
* @param inlineTop {String} inline css `top` for the original sourceNode
* @param relatives {Array} hash with all draggables that are being move togerther with the master draggable
* @private
* @since 0.0.1
*/
_defineDropEv: function(e, ddProps) {
console.log(NAME, '_defineDropEv '+ddProps.dragNode);
var instance = this;
instance.restoreDraggables = instance._restoreDraggables.bind(instance, e, ddProps);
Event.defineEvent(e.emitter+':'+DD_DROP)
.defaultFn(instance._defFnDrop.rbind(instance, ddProps))
.forceAssign(); // need to reassign, because all arguments need to be bound again and we need to override the definition of the `drag`-module
},
/**
* Defines the definition of the `dropzone` event.
* Also sets up listeners to tricker dd-over when the mouse is above an dropzone.
*
* @method _defineOverEv
* @param e {Object} eventobject
* @param dropzones {NodeList} list with dropzonenodes
* @private
* @since 0.0.1
*/
_defineOverEv: function(e, dropzones) {
console.log(NAME, '_defineOverEv');
var instance = this,
emitterName = e.emitter,
ddProps = instance.ddProps;
Event.defineEvent(emitterName+':'+DROPZONE_OVER)
.defaultFn(instance._defFnOver.bind(instance)); // no need to reassign
return Event.after([mobileEvents ? PANMOVE : MOUSEMOVE, DD_FAKE_MOUSEMOVE], function(e2) {
var overDropzone = false,
dragNode = ddProps.dragNode;
if (typeof e2.center==='object') {
e2.clientX = e2.center.x;
e2.clientY = e2.center.y;
}
ddProps.mouseOverNode = e.target;
if (e2.clientX) {
ddProps.xMouseLast = e2.clientX + window.getScrollLeft();
ddProps.yMouseLast = e2.clientY + window.getScrollTop();
}
dropzones.forEach(
function(dropzone) {
// don't do double:
if (dropzone === e.dropTarget) {
overDropzone = true;
return;
}
var dropzoneAccept = dropzone.getAttr(DZ_DROPZONE) || '',
dropzoneMove = REGEXP_MOVE.test(dropzoneAccept),
dropzoneCopy = REGEXP_COPY.test(dropzoneAccept),
dropzoneDefDraggable = dragNode.getAttr(DD_DROPZONE),
dragOverPromise, dragOutEvent, effectAllowed, emitterAllowed, dropzoneEmitter, xMouseLast, yMouseLast, dropzoneAllowed;
// check if the mouse is inside the dropzone
// also check if the mouse is inside the dragged node: the dragged node might have been constrained
// and check if the dragged node is effectAllowed to go into the dropzone
xMouseLast = ddProps.xMouseLast;
yMouseLast = ddProps.yMouseLast;
if (dropzone.insidePos(xMouseLast, yMouseLast) && dragNode.insidePos(xMouseLast, yMouseLast)) {
effectAllowed = (!dropzoneMove && !dropzoneCopy) || (dropzoneCopy && (dropEffect===COPY)) || (dropzoneMove && (dropEffect===MOVE));
dropzoneEmitter = instance.getDropzoneEmitter(dropzoneAccept);
emitterAllowed = !dropzoneEmitter || (dropzoneEmitter.contains(emitterName));
dropzoneAllowed = !dropzoneDefDraggable || ((dropzoneDefDraggable===TRUE) || dropzone.matchesSelector(dropzoneDefDraggable));
if (dropzoneAllowed && effectAllowed && emitterAllowed) {
overDropzone = true;
e.dropTarget = dropzone;
// mouse is in area of dropzone
dragOverPromise = Promise.manage();
e.dropzone = dragOverPromise;
dragOutEvent = Event.after(
[mobileEvents ? PANMOVE : MOUSEMOVE, DD_FAKE_MOUSEMOVE],
function() {
dragOverPromise.fulfill(false);
},
function(e3) {
var effectAllowed, dropzoneAccept, dropzoneMove, dropzoneCopy;
if (e3.type===DD_FAKE_MOUSEMOVE) {
dropzoneAccept = dropzone.getAttr(DZ_DROPZONE) || '';
dropzoneMove = REGEXP_MOVE.test(dropzoneAccept);
dropzoneCopy = REGEXP_COPY.test(dropzoneAccept);
effectAllowed = (!dropzoneMove && !dropzoneCopy) || (dropzoneCopy && (dropEffect===COPY)) || (dropzoneMove && (dropEffect===MOVE));
return !effectAllowed;
}
return !dropzone.insidePos((e3.clientX || e3.center.x)+window.getScrollLeft(), (e3.clientY || e3.center.y)+window.getScrollTop());
}
);
dragOverPromise.finally(
function(insideDropzone) {
dragOutEvent.detach();
insideDropzone || (e.dropTarget=null);
}
);
ddProps.dragOverList.push(dragOverPromise);
/**
* Emitted when the draggable gets inside a dropzone.
*
* @event *:dropzone-over
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the dropzone
* @param e.dragNode {HtmlElement} The HtmlElement that is being dragged
* @param e.dropzone {Promise} The Promise that gets fulfilled as soon as the draggable is dropped, or outside the dropzone
* Will fulfill with one argument: `onDropzone` {Boolean} when `true`, the draggable is dropped inside the dropzone, otherwise
* the draggable got outside the dropzone without beging dropped.
* @param e.dropTarget {HtmlElement} The dropzone HtmlElement. Equals e.target
* @param e.ctrlKey {Boolean} Whether the Ctrl/cmd key is pressed
* @param e.isCopied {Boolean} Whether the drag is a copy-drag
* @param [e.sourceNode] {HtmlElement} The original Element. Only when a `copy` is made --> e.dragNode is being moved while
* e.sourceNode stand at its place.
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating the draggable
* @param e.sourceTarget {HtmlElement} the deepest HtmlElement of the draggable where the mouse lies upon
* @param e.dd {Promise} Promise that gets fulfilled when dragging is ended. The fullfilled-callback has no arguments.
* @param e.xMouse {Number} the current x-position in the window-view
* @param e.yMouse {Number} the current y-position in the window-view
* @param e.clientX {Number} the current x-position in the window-view
* @param e.clientY {Number} the current y-position in the window-view
* @param e.xMouseOrigin {Number} the original x-position in the document when drag started (incl. scrollOffset)
* @param e.yMouseOrigin {Number} the original y-position in the document when drag started (incl. scrollOffset)
* @param [e.relatives] {NodeList} an optional list that the user could set in a `before`-subscriber of the `dd`-event
* to inform which nodes are related to the draggable node and should be dragged as well.
* @param [e.relativeDragNodes] {NodeList} an optional list that holds the HtmlElements that corresponds with
* the `e.relative` list, but is a list with draggable Elements.
* @since 0.1
*/
Event.emit(dropzone, emitterName+':'+DROPZONE_OVER, e);
}
}
}
);
overDropzone || (e.dropTarget=null);
});
},
/**
* Emits a dropzone-drop event.
*
* @method _emitDropzoneDrop
* @param e {Object} eventobject to pass arround
* @private
* @since 0.0.1
*/
_emitDropzoneDrop: function(e) {
/**
* Emitted when a draggable gets dropped inside a dropzone.
*
* @event *:dropzone-drop
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the dropzone
* @param e.dragNode {HtmlElement} The HtmlElement that is being dragged
* @param e.dropzone {Promise} The Promise that gets fulfilled as soon as the draggable is dropped, or outside the dropzone
* Will fulfill with one argument: `onDropzone` {Boolean} when `true`, the draggable is dropped inside the dropzone, otherwise
* the draggable got outside the dropzone without beging dropped.
* @param e.dropTarget {HtmlElement} The dropzone HtmlElement. Equals e.target
* @param e.ctrlKey {Boolean} Whether the Ctrl/cmd key is pressed
* @param e.isCopied {Boolean} Whether the drag is a copy-drag
* @param [e.sourceNode] {HtmlElement} The original Element. Only when a `copy` is made --> e.dragNode is being moved while
* e.sourceNode stand at its place.
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating the draggable
* @param e.sourceTarget {HtmlElement} the deepest HtmlElement of the draggable where the mouse lies upon
* @param e.dd {Promise} Promise that gets fulfilled when dragging is ended. The fullfilled-callback has no arguments.
* @param e.xMouse {Number} the current x-position in the window-view
* @param e.yMouse {Number} the current y-position in the window-view
* @param e.clientX {Number} the current x-position in the window-view
* @param e.clientY {Number} the current y-position in the window-view
* @param e.xMouseOrigin {Number} the original x-position in the document when drag started (incl. scrollOffset)
* @param e.yMouseOrigin {Number} the original y-position in the document when drag started (incl. scrollOffset)
* @param [e.relatives] {NodeList} an optional list that the user could set in a `before`-subscriber of the `dd`-event
* to inform which nodes are related to the draggable node and should be dragged as well.
* @param [e.relativeDragNodes] {NodeList} an optional list that holds the HtmlElements that corresponds with
* the `e.relative` list, but is a list with draggable Elements.
* @since 0.1
*/
Event.emit(e.dropTarget, e.emitter+':'+DROPZONE_DROP, e);
},
/**
* Sets the draggable node back to its original position
*
* @method _handleDrop
* @param e {Object} eventobject
* @param sourceNode {HtmlElement} the original HtmlElement
* @param dragNode {HtmlElement} the dragged HtmlElement (either original or clone)
* @param relatives {Array} hash with all draggables that are being move togerther with the master draggable
* @private
* @since 0.0.1
*/
_handleDrop: function(e, sourceNode, dragNode, relatives) {
console.log(NAME, '_handleDrop '+dragNode);
var instance = this,
dropzoneNode = e.dropTarget,
delegatedDragging = sourceNode.hasClass(DEL_DRAGGABLE),
constrainRectangle, borderLeft, borderTop, dragNodeX, dragNodeY, copyToDropzone, moveToDropzone,
moveInsideDropzone, isCopied, dropzoneDelegatedDraggable, dropzoneIsDelegated;
if (dropzoneNode) {
dropzoneDelegatedDraggable = dropzoneNode.getAttr(DD_MINUSDRAGGABLE);
dropzoneIsDelegated = dropzoneDelegatedDraggable && (dropzoneNode.getAttr(DD_MINUSDRAGGABLE)!=='true');
copyToDropzone = function(nodeSource, nodeDrag, shiftX, shiftY) {
if (delegatedDragging) {
if (!dropzoneIsDelegated) {
dragNode.getPlugin('dd').then(function(plugin) {
plugin.model[DRAGGABLE] = TRUE;
});
}
nodeDrag.removeClass(DEL_DRAGGABLE);
}
PLUGIN_ATTRS.forEach(function(attribute) {
var data = '_del_'+attribute,
attr = sourceNode.getData(data);
if (attr) {
if (dropzoneIsDelegated) {
nodeDrag.removeAttr(attribute);
}
else {
dragNode.getPlugin('dd').then(function(plugin) {
plugin.model[attribute] = attr;
});
}
nodeSource.getPlugin('dd').then(function(plugin) {
delete plugin.model[attribute];
});
nodeSource.removeData(data);
nodeDrag.removeData(data);
}
});
dropzoneNode.append(nodeDrag);
nodeDrag.removeClass([DD_OPACITY_CLASS, DD_TRANSITION_CLASS, HIGH_Z_CLASS, DD_DRAGGING_CLASS, NO_TRANS_CLASS, DD_MASTER_CLASS, DD_COPIED_CLASS]);
nodeSource.removeClass(DD_SOURCE_ISCOPIED_CLASS);
nodeDrag.setXY(dragNodeX+shiftX, dragNodeY+shiftY, constrainRectangle, true);
// make the new HtmlElement non-copyable: it only can be replaced inside its dropzone
if (!dropzoneIsDelegated) {
nodeDrag.getPlugin('dd').then(function(plugin) {
plugin.model[EFFECT_ALLOWED] = MOVE;
plugin.model[DROPZONE_MOVABLE] = TRUE;
});
}
};
moveToDropzone = function(nodeSource, nodeDrag, shiftX, shiftY) {
nodeSource.setInlineStyle(POSITION, ABSOLUTE);
if (delegatedDragging) {
if (!dropzoneIsDelegated) {
nodeSource.getPlugin('dd').then(function(plugin) {
plugin.model[DRAGGABLE] = TRUE;
});
}
nodeSource.removeClass(DEL_DRAGGABLE);
}
PLUGIN_ATTRS.forEach(function(attribute) {
var data = '_del_'+attribute,
attr = sourceNode.getData(data);
if (attr) {
if (dropzoneIsDelegated) {
nodeSource.removeAttr(attribute);
}
else {
nodeSource.getPlugin('dd').then(function(plugin) {
plugin.model[attribute] = attr;
});
}
nodeSource.removeData(data);
}
});
dropzoneNode.append(nodeSource);
nodeSource.setXY(dragNodeX+shiftX, dragNodeY+shiftY, constrainRectangle, true);
// make the new HtmlElement non-copyable: it only can be replaced inside its dropzone
if (!dropzoneIsDelegated) {
nodeSource.getPlugin('dd').then(function(plugin) {
plugin.model[EFFECT_ALLOWED] = MOVE;
plugin.model[DROPZONE_MOVABLE] = TRUE;
});
}
nodeSource.removeClass(DD_HIDDEN_SOURCE_CLASS);
nodeDrag.remove();
};
// reset its position, only now constrain it to the dropzondenode
// we need to specify exactly the droparea: because we don't want to compare to any
// scrollWidth/scrollHeight, but exaclty to the visible part of the dropzone
borderLeft = parseInt(dropzoneNode.getStyle(BORDER_LEFT_WIDTH), 10);
borderTop = parseInt(dropzoneNode.getStyle(BORDER_TOP_WIDTH), 10);
constrainRectangle = {
x: dropzoneNode.left + borderLeft,
y: dropzoneNode.top + borderTop,
w: dropzoneNode.offsetWidth - borderLeft - parseInt(dropzoneNode.getStyle(BORDER_RIGHT_WIDTH), 10),
h: dropzoneNode.offsetHeight - borderTop - parseInt(dropzoneNode.getStyle(BORDER_BOTTOM_WIDTH), 10)
};
isCopied = (ctrlPressed && instance.allowCopy(dragNode)) || instance.onlyCopy(dragNode);
if (isCopied) {
// backup x,y before move it into dropzone (which leads to new x,y)
dragNodeX = dragNode.left;
dragNodeY = dragNode.top;
// now move the dragNode into dropzone
relatives && relatives.forEach(
function(item) {
(dragNode!==item.dragNode) && copyToDropzone(item.sourceNode, item.dragNode, item.shiftX, item.shiftY);
}
);
copyToDropzone(sourceNode, dragNode, 0 ,0);
}
else {
dragNodeX = dragNode.left;
dragNodeY = dragNode.top;
relatives && relatives.forEach(
function(item) {
(dragNode!==item.dragNode) && moveToDropzone(item.sourceNode, item.dragNode, item.shiftX, item.shiftY);
}
);
moveToDropzone(sourceNode, dragNode, 0, 0);
}
sourceNode.removeClass(DEL_DRAGGABLE);
instance._emitDropzoneDrop(e);
}
else {
(dragNode.hasAttr(DD_DROPZONE_MOVABLE)) && (dropzoneNode=dragNode.inside(DROPZONE_BRACKETS));
if (dropzoneNode && dragNode.rectangleInside(dropzoneNode)) {
moveInsideDropzone = function(hasMatch, nodeSource, nodeDrag, shiftX, shiftY) {
hasMatch && nodeSource.setXY(nodeSource+shiftX, nodeSource+shiftY, constrainRectangle, true);
if (delegatedDragging) {
nodeSource.removeClass(DEL_DRAGGABLE);
}
PLUGIN_ATTRS.forEach(function(attribute) {
var data = '_del_'+attribute,
attr = dragNode.getData(data);
if (attr) {
if (dropzoneIsDelegated) {
nodeSource.removeAttr(attribute);
}
else {
nodeSource.getPlugin('dd').then(function(plugin) {
plugin.model[attribute] = attr;
});
}
nodeSource.removeData(data);
}
});
nodeSource.removeClass(DD_HIDDEN_SOURCE_CLASS);
nodeDrag.remove();
};
// reset its position, only now constrain it to the dropzondenode
// we need to specify exactly the droparea: because we don't want to compare to any
// scrollWidth/scrollHeight, but exaclty to the visible part of the dropzone
dropzoneDelegatedDraggable = dropzoneNode.getAttr(DD_MINUSDRAGGABLE);
dropzoneIsDelegated = dropzoneDelegatedDraggable && (dropzoneNode.getAttr(DD_MINUSDRAGGABLE)!=='true');
borderLeft = parseInt(dropzoneNode.getStyle(BORDER_LEFT_WIDTH), 10);
borderTop = parseInt(dropzoneNode.getStyle(BORDER_TOP_WIDTH), 10);
constrainRectangle = {
x: dropzoneNode.left + borderLeft,
y: dropzoneNode.top + borderTop,
w: dropzoneNode.offsetWidth - borderLeft - parseInt(dropzoneNode.getStyle(BORDER_RIGHT_WIDTH), 10),
h: dropzoneNode.offsetHeight - borderTop - parseInt(dropzoneNode.getStyle(BORDER_BOTTOM_WIDTH), 10)
};
dragNodeX = dragNode.left;
dragNodeY = dragNode.top;
relatives && relatives.forEach(
function(item) {
(sourceNode!==item.sourceNode) && moveInsideDropzone(dropzoneNode, item.sourceNode, item.dragNode, item.shiftX, item.shiftY);
}
);
moveInsideDropzone(dropzoneNode, sourceNode, dragNode, 0, 0);
}
else {
instance.restoreDraggables();
}
}
sourceNode.removeClass(DD_MASTER_CLASS);
dragNode.removeClass(DD_MASTER_CLASS);
},
/**
* Sets the draggable items back to their original place. Should only be used when you prevent the default-function of `dd-drop`,
* so you can choose to do set the draggables back conditionally.
*
* @method _restoreDraggables
* @param e {Object} eventobject
* @param sourceNode {HtmlElement} the original HtmlElement
* @param dragNode {HtmlElement} the dragged HtmlElement (either original or clone)
* @param dropzoneSpecified {Boolean} whether the sourceNode had a dropzone specified
* @param x {Number} x-position in coordinaties relative to `document` (like getX())
* @param y {Number} y-position in coordinaties relative to `document` (like getX())
* @param inlineLeft {String} inline css `left` for the original sourceNode
* @param inlineTop {String} inline css `top` for the original sourceNode
* @param relatives {Array} hash with all draggables that are being move togerther with the master draggable
* @private
* @since 0.0.1
*/
_restoreDraggables: function(e, ddProps) {
console.log(NAME, '_restoreDraggables');
var instance = this,
sourceNode = ddProps.sourceNode,
dragNode = ddProps.dragNode,
dropzoneSpecified = ddProps.dropzoneSpecified,
x = ddProps.x,
y = ddProps.y,
inlineLeft = ddProps.inlineLeft,
inlineTop = ddProps.inlineTop,
relatives = ddProps.relatives;
instance.restoreDraggables = function() {/* NOOP */ return this;};
instance._setBack(e, sourceNode, dragNode, dropzoneSpecified, x, y, inlineLeft, inlineTop, e.dropzone);
relatives && relatives.forEach(
function(item) {
(dragNode!==item.dragNode) && instance._setBack(e, item.sourceNode, item.dragNode, dropzoneSpecified, x+item.shiftX, y+item.shiftY, item.inlineLeft, item.inlineTop);
}
);
return instance;
},
/**
* Sets the draggable node back to its original position
*
* @method _setBack
* @param sourceNode {HtmlElement} the original HtmlElement
* @param dragNode {HtmlElement} the dragged HtmlElement (either original or clone)
* @param dropzoneSpecified {Boolean} whether the sourceNode had a dropzone specified
* @param x {Number} x-position in coordinaties relative to `document` (like getX())
* @param y {Number} y-position in coordinaties relative to `document` (like getX())
* @param inlineLeft {String} inline css `left` for the original sourceNode
* @param inlineTop {String} inline css `top` for the original sourceNode
* @param [emitDropzoneEvent] {Boolean} whether dropzone-event should be emitted
* @private
* @since 0.0.1
*/
_setBack: function(e, sourceNode, dragNode, dropzoneSpecified, x, y, inlineLeft, inlineTop, emitDropzoneEvent) {
console.log(NAME, '_setBack to '+x+', '+y);
var tearedDown,
winScrollTop,
winScrollLeft,
dropzones,
tearDown = function() {
// dragNode might be gone when this method is called for the second time
// therefor check its existance:
if (!tearedDown) {
tearedDown = true;
// notransRemoval || (dragNode.removeEventListener && dragNode.removeEventListener(TRANS_END, tearDown, true));
if (dropzoneSpecified) {
sourceNode.removeClass([DD_HIDDEN_SOURCE_CLASS, DEL_DRAGGABLE, DD_MASTER_CLASS, DD_SOURCE_ISCOPIED_CLASS]);
dragNode.remove();
}
else {
dragNode.removeClass([DD_TRANSITION_CLASS, HIGH_Z_CLASS, DD_DRAGGING_CLASS, DEL_DRAGGABLE, DD_MASTER_CLASS, DD_SOURCE_ISCOPIED_CLASS]);
dragNode.setInlineStyle(LEFT, inlineLeft)
.setInlineStyle(TOP, inlineTop);
}
PLUGIN_ATTRS.forEach(function(attribute) {
var data = '_del_'+attribute;
if (sourceNode.getData(data)) {
sourceNode.removeData(data);
sourceNode.getPlugin('dd').then(function(plugin) {
delete plugin.model[attribute];
});
}
});
}
};
dragNode.removeClass([NO_TRANS_CLASS, DD_DRAGGING_CLASS]);
dragNode.setClass(DD_TRANSITION_CLASS);
// transitions only work with IE10+, and that browser has addEventListener
// when it doesn't have, it doesn;t harm to leave the transitionclass on: it would work anyway
// nevertheless we will remove it with a timeout
// if (dragNode.addEventListener) {
// dragNode.addEventListener(TRANS_END, tearDown, true);
// }
// ALWAYS tearDowm after delay --> when there was no repositioning, there never will be a transition-event
// LATER(tearDown, 260);
dragNode.setXY(x, y).finally(tearDown);
// now we might need to fire a last `dropzone` event when the dragged element returns to a dropzone when it wasn't before set it back
if (emitDropzoneEvent) {
dropzones = DOCUMENT.getAll(DROPZONE_BRACKETS);
if (dropzones) {
winScrollTop = window.getScrollTop();
winScrollLeft = window.getScrollLeft();
dropzones.forEach(
function(dropzone) {
if (dropzone.insidePos(x, y) && !dropzone.insidePos(e.xMouse+winScrollLeft, e.yMouse+winScrollTop)) {
e.dropTarget = dropzone;
e._noDDoutEvt = true;
Event.emit(dropzone, e.emitter+':'+DROPZONE_OVER, e);
}
}
);
}
}
},
/**
* Sets up a `keydown` and `keyup` listener, to monitor whether a `ctrlKey` (windows) or `metaKey` (Mac)
* is pressed to support the copying of draggable items
*
* @method _setupKeyEv
* @private
* @since 0.0.1
*/
_setupKeyEv: function() {
console.log(NAME, '_setupKeyEv');
var instance = this,
changeClasses = function(sourceNode, dragNode) {
sourceNode.toggleClass(DD_HIDDEN_SOURCE_CLASS, !ctrlPressed);
sourceNode.toggleClass(DD_SOURCE_ISCOPIED_CLASS, ctrlPressed);
dragNode.toggleClass([DD_OPACITY_CLASS, DD_COPIED_CLASS], ctrlPressed);
};
Event.after([KEY+DOWN, KEY+UP], function(e) {
console.log(NAME, 'event '+e.type);
var ddProps = instance.ddProps,
sourceNode = ddProps.sourceNode,
dragNode, mouseOverNode;
ctrlPressed = e.ctrlKey || e.metaKey;
if (sourceNode && instance.allowSwitch(sourceNode)) {
dragNode = ddProps.dragNode;
mouseOverNode = ddProps.mouseOverNode;
dropEffect = ctrlPressed ? COPY : MOVE;
changeClasses(sourceNode, dragNode);
ddProps.relatives && ddProps.relatives.forEach(
function(item) {
changeClasses(item.sourceNode, item.dragNode);
}
);
// now, it could be that any droptarget should change its appearance (DD_DROPACTIVE_CLASS).
// we need to recalculate it for all targets
// we do this by emitting a DD_FAKE_MOUSEMOVE event
/**
* Fired when the mouse comes back into the browser-window while dd-drag was busy yet no buttons are pressed.
* This is a correction to the fact that the mouseup-event wasn't noticed because the mouse was outside the browser.
*
* @event dd-fake-mousemove
* @private
* @since 0.1
*/
mouseOverNode && Event.emit(mouseOverNode, UI+':'+DD_FAKE_MOUSEMOVE);
}
});
},
/**
* Cleansup the dragover subscriber and fulfills any dropzone-promise.
*
* @method _teardownOverEvent
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_teardownOverEvent: function(e, ddProps) {
console.log('_teardownOverEvent');
var dragOverEvent = ddProps.dragOverEv,
mouseX = e.xMouse,
mouseY = e.yMouse,
winScrollTop, winScrollLeft;
if (dragOverEvent) {
dragOverEvent.detach();
winScrollTop = window.getScrollTop();
winScrollLeft = window.getScrollLeft();
ddProps.dragOverList.forEach(function(promise) {
promise.fulfill(e.dropTarget && e.dropTarget.insidePos(mouseX+winScrollLeft, mouseY+winScrollTop));
});
}
},
/**
* Returns true if the dropzone-HtmlElement accepts copy-dragables.
* Is determined by the attribute `dd-effect-allowed="copy"` or `dd-effect-allowed="all"`
*
* @method allowCopy
* @param dropzone {HtmlElement} HtmlElement that is checked for its allowed effects
* @return {Boolean} if copy-dragables are allowed
* @since 0.0.1
*/
allowCopy: function(dropzone) {
var allowedEffects = this._allowedEffects(dropzone);
console.log('allowCopy --> '+REGEXP_ALL.test(allowedEffects) || REGEXP_COPY.test(allowedEffects));
return REGEXP_ALL.test(allowedEffects) || REGEXP_COPY.test(allowedEffects);
},
/**
* Returns true if the dragable-HtmlElement allowes to switch between `copy` and `move`.
*
* @method allowSwitch
* @param dragableElement {HtmlElement} HtmlElement that is checked for its allowed effects
* @return {Boolean} if copy-dragables are allowed
* @since 0.0.1
*/
allowSwitch: function(dragableElement) {
console.log('allowSwitch --> '+REGEXP_ALL.test(this._allowedEffects(dragableElement)));
return REGEXP_ALL.test(this._allowedEffects(dragableElement));
},
/**
* Returns the emitterName that the dropzone accepts.
*
* @method getDropzoneEmitter
* @param dropzone {String} dropzone attribute of the dropzone HtmlElement
* @return {String|null} the emitterName that is accepted
* @since 0.0.1
*/
getDropzoneEmitter: function(dropzone) {
var extract = dropzone.match(REGEXP_EMITTER);
console.log('getDropzoneEmitter --> '+(extract && extract[1]));
return extract && (','+extract[1]+',');
},
/**
* Initializes dragdrop. Needs to be invoked, otherwise DD won't run.
*
* @method init (extended by drag-drop)
* @param dragableElement {HtmlElement} HtmlElement that is checked for its allowed effects
* @return {Boolean} if copy-dragables are allowed
* @since 0.0.1
*/
init: function() {
console.log(NAME, 'init');
var instance = this;
if (!instance._ddInited) {
// we will initialize `Drag` --> don;t worry if it was initialised before,
// Drag.init() will only run once
$superInit.call(instance);
instance._setupKeyEv();
instance.notify(function(e, ddProps) {
var dropzones, sourceNode,
dragNode = ddProps.dragNode,
dropzoneSpecified = ddProps.dropzoneSpecified = dragNode.hasAttr(DD_DROPZONE) || dragNode.hasAttr(DD_EMITTER) || (e.emitter!==UI),
setupDragnode = function(nodeSource, nodeDrag, shiftX, shiftY) {
if (dropEffect===COPY) {
nodeDrag.setClass([DD_OPACITY_CLASS, DD_COPIED_CLASS]);
nodeSource.setClass(DD_SOURCE_ISCOPIED_CLASS);
}
else {
nodeSource.setClass(DD_HIDDEN_SOURCE_CLASS);
}
nodeDrag.setClass(INVISIBLE_CLASS);
nodeDrag.setInlineStyle(POSITION, ABSOLUTE);
nodeSource.parentNode.append(nodeDrag, false, nodeSource);
nodeDrag.setXY(ddProps.xMouseLast+shiftX, ddProps.yMouseLast+shiftY, ddProps.constrain, true);
nodeDrag.removeClass(INVISIBLE_CLASS);
};
if (dropzoneSpecified) {
sourceNode = e.sourceNode = ddProps.sourceNode = ddProps.dragNode;
e.dragNode = ddProps.dragNode = ddProps.sourceNode.cloneNode(true);
// correct sourceNode class: reset CSS set by `drag`:
sourceNode.removeClass([NO_TRANS_CLASS, HIGH_Z_CLASS, DD_DRAGGING_CLASS]);
// also correct inline CSS style for `left` and `top` of the sourceNode:
sourceNode.setInlineStyle(LEFT, ddProps.inlineLeft);
sourceNode.setInlineStyle(TOP, ddProps.inlineTop);
dropEffect = (instance.onlyCopy(dragNode) || (ctrlPressed && instance.allowCopy(dragNode))) ? COPY : MOVE;
setupDragnode(ddProps.sourceNode, ddProps.dragNode, 0, 0);
if (ddProps.relatives) {
e.relativeDragNodes = [];
ddProps.relatives.forEach(
function(item) {
item.sourceNode = item.dragNode;
item.dragNode = item.dragNode.cloneNode(true);
setupDragnode(item.sourceNode, item.dragNode, item.shiftX, item.shiftY);
e.relativeDragNodes.push(item.dragNode);
}
);
}
dropzones = DOCUMENT.getAll(DROPZONE_BRACKETS);
if (dropzones.length>0) {
// create a custom over-event that fires exactly when the mouse is over any dropzone
// we cannot use `hover`, because that event fails when there is an absolute floated element outsize `dropzone`
// lying on top of the dropzone. -> we need to check by coördinates
ddProps.dragOverEv = instance._defineOverEv(e, dropzones);
}
}
else {
e.dragNode = ddProps.dragNode;
}
ddProps.dragDropEv = instance._defineDropEv(e, ddProps);
}, instance, true);
instance.notify(instance._teardownOverEvent, instance);
}
instance._ddInited = true;
},
/**
* Returns true if the dragable-HtmlElement accepts only copy-dragables (no moveable)
* Is determined by the attribute `dd-effect-allowed="copy"`
*
* @method onlyCopy
* @param dragableElement {HtmlElement} HtmlElement that is checked for its allowed effects
* @return {Boolean} if only copy-dragables are allowed
* @since 0.0.1
*/
onlyCopy: function(dragableElement) {
console.log('onlyCopy --> '+REGEXP_COPY.test(this._allowedEffects(dragableElement)));
return REGEXP_COPY.test(this._allowedEffects(dragableElement));
},
/**
* Sets the draggable items back to their original place. Should only be used when you prevent the default-function of `dd-drop`,
* so you can choose to do set the draggables back conditionally.
*
* @method restoreDraggables
* @private
* @chainable
* @since 0.0.1
*/
restoreDraggables: function() {/* NOOP */ return this;}
};
DragModule.merge(DD, {force: true});
window._ITSAPlugins.dd.mergePrototypes({
attrs: {
draggable: 'string',
handle: 'string',
emitter: 'string',
'effect-allowed': 'string',
'dropzone-movable': 'string',
dropzone: 'string'
}
}, true);
DOCUMENT.definePlugin('dz', null, {
attrs: {
dropzone: 'string'
},
defaults: {
dropzone: 'true'
}
});
window._ITSAmodules.DragDrop = DragModule;
return DragModule;
};
},{"./css/drag-drop.css":1937,"drag":1940,"event-dom":2308,"js-ext":2404,"js-ext/extra/hashmap.js":2403,"node-plugin":2420,"polyfill/polyfill-base.js":2592,"useragent":5849,"vdom":2645,"window-ext":2646}],1939:[function(require,module,exports){
module.exports=require(265)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],1940:[function(require,module,exports){
arguments[4][266][0].apply(exports,arguments)
},{"./css/drag.css":1939,"event-dom":1941,"js-ext":2037,"js-ext/extra/hashmap.js":2036,"node-plugin":2053,"polyfill":2225,"useragent":2241,"vdom":2294,"window-ext":2295}],1941:[function(require,module,exports){
"use strict";
/**
* Integrates DOM-events to event. more about DOM-events:
* http://www.smashingmagazine.com/2013/11/12/an-introduction-to-dom-events/
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @example
* Event = require('event-dom')(window);
*
* @module event
* @submodule event-dom
* @class Event
* @since 0.0.1
*/
var NAME = '[event-dom]: ',
Event = require('event'),
later = require('utils').later,
createHashMap = require('js-ext/extra/hashmap.js').createMap,
OUTSIDE = 'outside',
REGEXP_NODE_ID = /^#\S+$/,
REGEXP_EXTRACT_NODE_ID = /#(\S+)/,
REGEXP_UI_OUTSIDE = /^.+outside$/,
TIME_BTN_PRESSED = 200,
PURE_BUTTON_ACTIVE = 'pure-button-active',
UI = 'UI:',
NODE = 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
TAP = 'tap',
CLICK = 'click',
RIGHTCLICK = 'right'+CLICK,
CENTERCLICK = 'center'+CLICK,
EV_REMOVED = UI+NODE+REMOVE,
EV_INSERTED = UI+NODE+INSERT,
EV_CONTENT_CHANGE = UI+NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = UI+ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = UI+ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = UI+ATTRIBUTE+INSERT,
mutationEventsDefined = false,
NO_DEEP_SEARCH = {},
/*
* Internal hash containing all DOM-events that are listened for (at `document`).
*
* DOMEvents = {
* 'tap': callbackFn,
* 'mousemove': callbackFn,
* 'keypress': callbackFn
* }
*
* @property DOMEvents
* @default {}
* @type Object
* @private
* @since 0.0.1
*/
DOMEvents = {};
require('js-ext/lib/string.js');
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('polyfill/polyfill-base.js');
module.exports = function (window) {
var DOCUMENT = window.document,
_domSelToFunc, _evCallback, _findCurrentTargets, _preProcessor, _setupEvents, _setupMutationListener, _teardownMutationListener,
_setupDomListener, _teardownDomListener, SORT, _sortFunc, _sortFuncReversed, _getSubscribers, _selToFunc, MUTATION_EVENTS, preventClick;
require('vdom')(window);
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.EventDom) {
return Event; // Event was already extended
}
console.warn('event-dom 7');
MUTATION_EVENTS = [EV_REMOVED, EV_INSERTED, EV_CONTENT_CHANGE, EV_ATTRIBUTE_REMOVED, , EV_ATTRIBUTE_CHANGED, EV_ATTRIBUTE_INSERTED];
/*
* Transforms the selector to a valid function
*
* @method _selToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_selToFunc = function(customEvent, subscriber) {
Event._sellist.some(function(selFn) {
return selFn(customEvent, subscriber);
});
};
/*
* Creates a filterfunction out of a css-selector. To be used for catching any dom-element, without restrictions
* of any context (like Parcels can --> Parcel.Event uses _parcelSelToDom instead)
* On "non-outside" events, subscriber.t is set to the node that first matches the selector
* so it can be used to set as e.target in the final subscriber
*
* @method _domSelToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_domSelToFunc = function(customEvent, subscriber) {
// this stage is runned during subscription
var outsideEvent = REGEXP_UI_OUTSIDE.test(customEvent),
selector = subscriber.f,
context = subscriber.o,
vnode = subscriber.o.vnode,
isCustomElement = vnode && vnode.isItag,
visibleContent = isCustomElement && !vnode.domNode.contentHidden,
nodeid, byExactId, newTarget, deepSearch;
isCustomElement = false;
console.log(NAME, '_domSelToFunc type of selector = '+typeof selector);
// note: selector could still be a function: in case another subscriber
// already changed it.
if ((!selector && !isCustomElement) || (typeof selector === 'function')) {
subscriber.n || (subscriber.n = isCustomElement ? context : DOCUMENT);
return true;
}
selector || (selector='');
nodeid = selector.match(REGEXP_EXTRACT_NODE_ID);
nodeid ? (subscriber.nId=nodeid[1]) : (subscriber.n=isCustomElement ? context : DOCUMENT);
byExactId = REGEXP_NODE_ID.test(selector);
deepSearch = !NO_DEEP_SEARCH[customEvent];
// set the selector to `subscriber._s` so that e.currentTarget can calculate it:
subscriber._s = selector;
subscriber.f = function(e) {
// this stage is runned when the event happens
console.log(NAME, '_domSelToFunc inside filter. selector: '+selector);
var node = e.target,
vnode = node.vnode,
character1 = selector && selector.substr(1),
match = false;
if (!isCustomElement || visibleContent || context.contains(node)) {
if (selector==='') {
match = true;
}
else {
// e.target is the most deeply node in the dom-tree that caught the event
// our listener uses `selector` which might be a node higher up the tree.
// we will reset e.target to this node (if there is a match)
// note that e.currentTarget will always be `document` --> we're not interested in that
// also, we don't check for `node`, but for node.matchesSelector: the highest level `document`
// is not null, yet it doesn't have .matchesSelector so it would fail
// we DON'T want this for the focus and blur event!
if (deepSearch) {
if (vnode) {
// we go through the vdom
if (!vnode.removedFromDOM) {
while (vnode && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = vnode.domNode;
}
vnode = vnode.vParent;
}
}
}
else {
// we go through the dom
while (node.matchesSelector && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the dom');
match = byExactId ? (node.id===character1) : node.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = node;
}
node = node.parentNode;
}
}
}
else {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
if (vnode) {
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
}
else {
match = node.matchesSelector && (byExactId ? (node.id===character1) : node.matchesSelector(selector));
}
match && (e.sourceTarget=node);
}
}
}
if (outsideEvent && !match) {
// there is a match for the outside-event:
// we need to set e.sourceTarget and e.target:
newTarget = DOCUMENT.getElement(selector, true);
if (newTarget) {
e.sourceTarget = node;
subscriber.t = newTarget;
}
else {
// make return `false` because the selector is not in the dom
match = true;
}
}
console.log(NAME, '_domSelToFunc filter returns '+(!outsideEvent ? match : !match));
return !outsideEvent ? match : !match;
};
return true;
};
// at this point, we need to find out what are the current node-refs. whenever there is
// a filter that starts with `#` --> in those cases we have a bubble-chain, because the selector isn't
// set up with `document` at its root.
// we couldn't do this at time of subscribtion, for the nodes might not be there at that time.
// however, we only need to do this once: we store the value if we find them
// no problem when the nodes leave the dom later: the previous filter wouldn't pass
_findCurrentTargets = function(subscribers) {
console.log(NAME, '_findCurrentTargets');
subscribers.forEach(
function(subscriber) {
console.log(NAME, '_findCurrentTargets for single subscriber. nId: '+subscriber.nId);
subscriber.nId && (subscriber.n=DOCUMENT.getElementById(subscriber.nId, true));
}
);
};
/*
* Generates an event through our Event-system. Does the actual transportation from DOM-events
* into our Eventsystem. It also looks at the response of our Eventsystem: if our system
* halts or preventDefaults the customEvent, then the original DOM-event will be preventDefaulted.
*
* @method _evCallback
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_evCallback = function(e) {
console.log(NAME, '_evCallback');
var allSubscribers = Event._subs,
eType = e.type,
eventobject, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs, subsOutside,
subscribers, eventobjectOutside, wildcard_named_subsOutside, customEvent, eventName, which;
eventName = eType;
// first: a `click` event might be needed to transformed into `rightclick`:
if (eventName===CLICK) {
which = e.which;
(which===2) && (eventName=CENTERCLICK);
(which===3) && (eventName=RIGHTCLICK);
}
if (eventName==='tap') {
// prevent the next click-event
preventClick = true;
e.clientX || (e.clientX = e.center && e.center.x);
e.clientY || (e.clientY = e.center && e.center.y);
}
else if (preventClick && (eventName===CLICK)) {
preventClick = false;
return;
}
if (eventName===CLICK) {
eventName = 'tap';
e.center = {
x: e.clientX,
y: e.clientY
};
e.eventType = 4;
e.pointerType = 'mouse';
e.tapCount = 1;
}
customEvent = 'UI:'+eventName;
subs = allSubscribers[customEvent];
wildcard_named_subs = allSubscribers['*:'+eventName];
named_wildcard_subs = allSubscribers['UI:*'];
wildcard_wildcard_subs = allSubscribers['*:*'];
// Emit the dom-event though our eventsystem:
// NOTE: emit() needs to be synchronous! otherwise we wouldn't be able
// to preventDefault in time
//
// e = eventobject from the DOM-event OR gesture-event
// eventobject = eventobject from our Eventsystem, which get returned by calling `emit()`
// now so the work:
subscribers = _getSubscribers(e, true, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
eventobject = Event._emit(e.target, customEvent, e, subscribers, [], _preProcessor, false, true);
// now check outside subscribers
subsOutside = allSubscribers[customEvent+OUTSIDE];
wildcard_named_subsOutside = allSubscribers['*:'+eventName+OUTSIDE];
subscribers = _getSubscribers(e, true, subsOutside, wildcard_named_subsOutside);
eventobjectOutside = Event._emit(e.target, customEvent+OUTSIDE, e, subscribers, [], _preProcessor, false, true);
// if eventobject was preventdefaulted or halted: take appropriate action on
// the original dom-event. Note: only the original event can caused this, not the outsideevent
// stopPropagation on the original eventobject has no impact on our eventsystem, but who know who else is watching...
// be carefull though: not all gesture events have e.stopPropagation
eventobject.status.halted && e.stopPropagation && e.stopPropagation();
// now we might need to preventDefault the original event.
// be carefull though: not all gesture events have e.preventDefault
if ((eventobject.status.halted || eventobject.status.defaultPrevented || eventobject.status.defaultPreventedContinue) && e.preventDefault) {
e.preventDefault();
}
if (eventobject.status.ok) {
// last step: invoke the aftersubscribers
// we need to do this asynchronous: this way we pass them AFTER the DOM-event's defaultFn
// also make sure to paas-in the payload of the manipulated eventobject
subscribers = _getSubscribers(e, false, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent, eventobject, [], subscribers, _preProcessor, true), 10);
// now check outside subscribers
subscribers = _getSubscribers(e, false, subsOutside, wildcard_named_subsOutside);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent+OUTSIDE, eventobjectOutside, [], subscribers, _preProcessor, true), 10);
}
};
/*
* Creates an array of subscribers in the right order, conform their position in the DOM.
* Only subscribers that match the filter are involved.
*
* @method _getSubscribers
* @param e {Object} eventobject
* @param before {Boolean} whether it is a before- or after-subscriber
* @param subs {Array} array with subscribers
* @param wildcard_named_subs {Array} array with subscribers
* @param named_wildcard_subs {Array} array with subscribers
* @param wildcard_wildcard_subs {Array} array with subscribers
* @private
* @since 0.0.1
*/
_getSubscribers = function(e, before, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs) {
var subscribers = [],
beforeOrAfter = before ? 'b' : 'a',
saveConcat = function(extrasubs) {
extrasubs && extrasubs[beforeOrAfter] && (subscribers=subscribers.concat(extrasubs[beforeOrAfter]));
};
saveConcat(subs);
saveConcat(wildcard_named_subs);
saveConcat(named_wildcard_subs);
saveConcat(wildcard_wildcard_subs);
if (subscribers.length>0) {
subscribers = function(array, testFunc) {
// quickest way to filter an array: see http://jsperf.com/array-filter-performance/4
var filtered = array.slice(0), i;
for (i=array.length-1; i>=0; i--) {
console.log(NAME, 'filtercheck for subscriber');
testFunc(array[i]) || filtered.splice(i, 1);
}
return filtered;
}(subscribers, function(subscriber) {return (!subscriber.f || subscriber.f.call(subscriber.o, e));});
if (subscribers.length>0) {
// _findCurrentTargets(subscribers);
// sorting, based upon the sortFn
subscribers.sort(SORT);
}
}
return subscribers;
};
/*
* Sets e.target and e.sourceTarget for the single subscriber.
* Needs to be done for evenry single subscriber, because with a single event, these values change for each subscriber
*
* @method _preProcessor
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_preProcessor = function(subscriber, e) {
console.log(NAME, '_preProcessor');
// inside the aftersubscribers, we may need exit right away.
// this would be the case whenever stopPropagation or stopImmediatePropagation was called
// in case the subscribernode equals the node on which stopImmediatePropagation was called: return true
var propagationStopped, immediatePropagationStopped,
targetnode = (subscriber.t || subscriber.n);
immediatePropagationStopped = e.status.immediatePropagationStopped;
if (immediatePropagationStopped && ((immediatePropagationStopped===targetnode) || !immediatePropagationStopped.contains(targetnode))) {
console.log(NAME, '_preProcessor will return true because of immediatePropagationStopped');
return true;
}
// in case the subscribernode does not fall within or equals the node on which stopPropagation was called: return true
propagationStopped = e.status.propagationStopped;
if (propagationStopped && (propagationStopped!==targetnode) && !propagationStopped.contains(targetnode)) {
console.log(NAME, '_preProcessor will return true because of propagationStopped');
return true;
}
e._s = subscriber._s;
// now we might need to set e.target to the right node:
// the filterfunction might have found the true domnode that should act as e.target
// and set it at subscriber.t
// also, we need to backup the original e.target: this one should be reset when
// we encounter a subscriber with its own filterfunction instead of selector
if (subscriber.t) {
e.sourceTarget || (e.sourceTarget=e.target);
e.target = subscriber.t;
}
else {
e.sourceTarget && (e.target=e.sourceTarget);
}
return false;
};
/*
* Transports DOM-events to the Event-system. Catches events at their most early stage:
* their capture-phase. When these events happen, a new customEvent is generated by our own
* Eventsystem, by calling _evCallback(). This way we keep DOM-events and our Eventsystem completely separated.
*
* @method _setupDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_setupDomListener = function(customEvent, subscriber) {
console.log(NAME, '_setupDomListener');
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0],
eventName = eventSplitted[1],
outsideEvent = REGEXP_UI_OUTSIDE.test(eventName);
// be careful: anyone could also register an `outside`-event.
// in those cases, the DOM-listener must be set up without `outside`
outsideEvent && (eventName=eventName.substring(0, eventName.length-7));
// if eventName equals `mouseover` or `mouseleave` then we quit:
// people should use `mouseover` and `mouseout`
if ((eventName==='mouseenter') || (eventName==='mouseleave')) {
console.warn(NAME, 'Subscription to '+eventName+' not supported, use mouseover and mouseout: this eventsystem uses these non-noisy so they act as mouseenter and mouseleave');
return;
}
// only accept tap-events, yet later on we WILL need to listen for click-events
(eventName===CLICK) && (eventName=TAP);
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(emitterName+':'+eventName+(outsideEvent ? OUTSIDE : ''), subscriber);
// already registered? then return, also return if someone registered for UI:*
if (DOMEvents[eventName] || (eventName==='*')) {
// cautious: one might have registered the event, but not yet the outsideevent.
// in that case: save this setting:
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
return;
}
DOMEvents[eventName] = true;
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
// one exception: windowresize should listen to the window-object
if (eventName==='resize') {
window.addEventListener(eventName, _evCallback);
}
else {
((eventName===RIGHTCLICK) || (eventName===CENTERCLICK)) && (eventName=TAP);
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.addEventListener(eventName, _evCallback, true);
// listen for both `tap` and `click` events to happen
(eventName===TAP) && DOCUMENT.addEventListener(CLICK, _evCallback, true);
}
};
_setupEvents = function() {
var lastFocussed;
// make sure disabled buttons don't work:
Event.before(['tap', 'press'], function(e) {
e.preventDefault();
}, '.pure-button-disabled, button[disabled]');
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.before(
'keydown',
function(e) {
e._buttonPressed = true;
Event.emit(e.target, 'UI:tap', e);
},
function(e) {
var keyCode = e.keyCode;
return (e.target.getTagName()==='BUTTON') && ((keyCode===13) || (keyCode===32));
}
);
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.after(
'tap',
function(e) {
var buttonNode = e.target;
if (e._buttonPressed) {
buttonNode.setClass(PURE_BUTTON_ACTIVE);
e._noRender = true;
// even if the node isn't in the DOM, we can still try to manipulate it:
// the vdom makes sure no errors occur when the node is already removed
later(buttonNode.removeClass.bind(buttonNode, PURE_BUTTON_ACTIVE), TIME_BTN_PRESSED);
}
}
);
// fix activeElement on Mac
Event.before('focus', function(e) {
// will come here more often because of bubblechain
// however, the last pass-through will set the deepest node
lastFocussed = e.target;
});
// patching DOCUMENT.activeElement because it doesn't work well in a Mac: https://developer.mozilla.org/en-US/docs/Web/API/document.activeElement
// DOCUMENT._activeElement is used with the patch for DOCUMENT.activeElement its getter
Event.after('focus', function() {
DOCUMENT._activeElement = lastFocussed;
});
Event.before('blur', function() {
DOCUMENT._activeElement = null;
});
// Note: window.document has no prototype
Object.defineProperty(DOCUMENT, 'activeElement', {
get: function() {
return DOCUMENT._activeElement || DOCUMENT.body;
}
});
};
_setupMutationListener = function() {
DOCUMENT.hasMutationSubs = true;
if (!mutationEventsDefined) {
Event.defineEvent(EV_REMOVED).unPreventable();
Event.defineEvent(EV_INSERTED).unPreventable();
Event.defineEvent(EV_CONTENT_CHANGE).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_REMOVED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_CHANGED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_INSERTED).unPreventable();
mutationEventsDefined = true;
}
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFunc = function(subscriberOne, subscriberTwo) {
return (subscriberTwo.t || subscriberTwo.n).contains(subscriberOne.t || subscriberOne.n) ? -1 : 1;
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFuncReversed = function(subscriberOne, subscriberTwo) {
return (subscriberOne.t || subscriberOne.n).contains(subscriberTwo.t || subscriberTwo.n) ? 1 : -1;
};
/*
* Removes DOM-eventsubscribers from document when they are no longer needed.
*
* @method _teardownDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @private
* @since 0.0.2
*/
_teardownDomListener = function(customEvent) {
var customEventWithoutOutside = customEvent.endsWith(OUTSIDE) ? customEvent.substr(0, customEvent.length-7) : customEvent,
eventSplitted = customEventWithoutOutside.split(':'),
eventName = eventSplitted[1],
stillInUse;
if ((customEventWithoutOutside===CLICK) || (customEventWithoutOutside===RIGHTCLICK) || (customEventWithoutOutside===CENTERCLICK)) {
stillInUse = Event._subs[CLICK] ||
Event._subs[CLICK+OUTSIDE];
Event._subs[RIGHTCLICK] ||
Event._subs[RIGHTCLICK+OUTSIDE],
Event._subs[CENTERCLICK] ||
Event._subs[CENTERCLICK+OUTSIDE];
eventName = CLICK;
}
else {
stillInUse = Event._subs[customEventWithoutOutside] || Event._subs[customEventWithoutOutside+OUTSIDE];
}
if (!stillInUse) {
console.log(NAME, '_teardownDomListener '+customEvent);
// remove eventlistener from `document`
// one exeption: windowresize should listen to the window-object
if (eventName==='resize') {
window.removeEventListener(eventName, _evCallback);
}
else {
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.removeEventListener(eventName, _evCallback, true);
}
delete DOMEvents[eventName];
}
};
_teardownMutationListener = function() {
if (!Event._subs[EV_REMOVED] &&
!Event._subs[EV_INSERTED] &&
!Event._subs[EV_CONTENT_CHANGE] &&
!Event._subs[EV_ATTRIBUTE_REMOVED] &&
!Event._subs[EV_ATTRIBUTE_CHANGED] &&
!Event._subs[EV_ATTRIBUTE_INSERTED]
) {
DOCUMENT.hasMutationSubs = false;
}
};
// Now a very tricky one:
// Some browsers do an array.sort down-top instead of top-down.
// In those cases we need another sortFn, for the position on an equal match should fall
// behind instead of before (which is the case on top-down sort)
[1,2].sort(function(a /*, b */) {
SORT || (SORT=(a===2) ? _sortFuncReversed : _sortFunc);
});
// Now we do some initialization in order to make DOM-events work:
Object.defineProperty(Event._defaultEventObj, 'currentTarget', {
get: function() {
var ev = this,
e_target = ev.target;
if (e_target && ev._s) {
if (e_target.matches(ev._s)) {
return e_target;
}
return e_target.inside(ev._s) || undefined;
}
}
});
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(UI+'*', _setupDomListener, Event)
._setEventObjProperty('stopPropagation', function() {this.status.ok || (this.status.propagationStopped = this.target);})
._setEventObjProperty('stopImmediatePropagation', function() {this.status.ok || (this.status.immediatePropagationStopped = this.target);});
// Notify when someone subscribes to any event at all --> we might need to transform the filterFn from a selector into a true fnction
// this is already done automaticly by _setupDomListener fo UI:* events
Event.notify('*:*', function(customEvent, subscriber) {
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0];
if ((emitterName!=='UI') && (typeof subscriber.f==='string')) {
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(customEvent, subscriber);
}
}, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(UI+'*', _teardownDomListener, Event);
Event._sellist = [_domSelToFunc];
_setupEvents();
// making HTMLElement to be able to emit using event-emitter:
(function(HTMLElementPrototype) {
HTMLElementPrototype.merge(Event.Emitter('UI'));
}(window.HTMLElement.prototype));
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(MUTATION_EVENTS, _setupMutationListener, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(MUTATION_EVENTS, _teardownMutationListener, Event);
// Note: window.document has no prototype
DOCUMENT.suppressMutationEvents = function(suppress) {
this._suppressMutationEvents = suppress;
};
// Event.noDeepDomEvt and Event._domCallback are the only method that is added to Event.
// We need to do this, because `event-mobile` needs access to the same method.
// We could have done without this method and instead listen for a custom-event to handle
// Mobile events, however, that would lead into 2 eventcycli which isn't performant.
/**
*
* @method noDeepDomEvt
* @param domEvent {String} the eventName that should be processed without deepsearch
* @param e {Object} eventobject
* @for Event
* @chainable
* @since 0.0.1
*/
Event.noDeepDomEvt = function(domEvent) {
domEvent.contains(':') || (domEvent=UI+domEvent);
NO_DEEP_SEARCH[domEvent] = true;
return this;
};
/**
* Does the actual transportation from DOM-events into the Eventsystem. It also looks at the response of
* the Eventsystem: on e.halt() or e.preventDefault(), the original DOM-event will be preventDefaulted.
*
* @method _domCallback
* @param eventName {String} the customEvent that is transported to the eventsystem
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
Event._domCallback = function(e) {
_evCallback(e);
};
// store module:
window._ITSAmodules.EventDom = Event;
return Event;
};
},{"event":1945,"js-ext/extra/hashmap.js":1961,"js-ext/lib/array.js":1962,"js-ext/lib/object.js":1963,"js-ext/lib/string.js":1964,"polyfill/polyfill-base.js":1976,"utils":1977,"vdom":2035}],1942:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":1947,"js-ext/lib/object.js":1948,"polyfill/polyfill-base.js":1960}],1943:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":1942}],1944:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":1942,"js-ext/extra/classes.js":1946,"js-ext/lib/object.js":1948}],1945:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":1942,"./event-emitter.js":1943,"./event-listener.js":1944}],1946:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":1948,"js-ext/extra/hashmap.js":1947,"polyfill/polyfill-base.js":1951}],1947:[function(require,module,exports){
module.exports=require(4)
},{}],1948:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1947,"polyfill/polyfill-base.js":1951,"utils":1952}],1949:[function(require,module,exports){
module.exports=require(14)
},{}],1950:[function(require,module,exports){
module.exports=require(15)
},{}],1951:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1949,"./lib/window.console.js":1950}],1952:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1953,"./lib/timers.js":1954}],1953:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1947,"polyfill/polyfill-base.js":1957}],1954:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1957}],1955:[function(require,module,exports){
module.exports=require(14)
},{}],1956:[function(require,module,exports){
module.exports=require(15)
},{}],1957:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1955,"./lib/window.console.js":1956}],1958:[function(require,module,exports){
module.exports=require(14)
},{}],1959:[function(require,module,exports){
module.exports=require(15)
},{}],1960:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1958,"./lib/window.console.js":1959}],1961:[function(require,module,exports){
module.exports=require(4)
},{}],1962:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":1967}],1963:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1961,"polyfill/polyfill-base.js":1967,"utils":1968}],1964:[function(require,module,exports){
module.exports=require(29)
},{}],1965:[function(require,module,exports){
module.exports=require(14)
},{}],1966:[function(require,module,exports){
module.exports=require(15)
},{}],1967:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1965,"./lib/window.console.js":1966}],1968:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1969,"./lib/timers.js":1970}],1969:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1961,"polyfill/polyfill-base.js":1973}],1970:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1973}],1971:[function(require,module,exports){
module.exports=require(14)
},{}],1972:[function(require,module,exports){
module.exports=require(15)
},{}],1973:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1971,"./lib/window.console.js":1972}],1974:[function(require,module,exports){
module.exports=require(14)
},{}],1975:[function(require,module,exports){
module.exports=require(15)
},{}],1976:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1974,"./lib/window.console.js":1975}],1977:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1978,"./lib/timers.js":1979}],1978:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1961,"polyfill/polyfill-base.js":1982}],1979:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":1982}],1980:[function(require,module,exports){
module.exports=require(14)
},{}],1981:[function(require,module,exports){
module.exports=require(15)
},{}],1982:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1980,"./lib/window.console.js":1981}],1983:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],1984:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":1988,"js-ext/extra/hashmap.js":1985,"polyfill/polyfill-base.js":1994}],1985:[function(require,module,exports){
module.exports=require(4)
},{}],1986:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":1987,"../lib/object.js":1988,"./classes.js":1984,"js-ext/extra/hashmap.js":1985,"polyfill/lib/weakmap.js":1992}],1987:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":1994}],1988:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":1985,"polyfill/polyfill-base.js":1994,"utils":1995}],1989:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":1994}],1990:[function(require,module,exports){
module.exports=require(29)
},{}],1991:[function(require,module,exports){
module.exports=require(14)
},{}],1992:[function(require,module,exports){
module.exports=require(57)
},{}],1993:[function(require,module,exports){
module.exports=require(15)
},{}],1994:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1991,"./lib/window.console.js":1993}],1995:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":1996,"./lib/timers.js":1997}],1996:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1985,"polyfill/polyfill-base.js":2000}],1997:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2000}],1998:[function(require,module,exports){
module.exports=require(14)
},{}],1999:[function(require,module,exports){
module.exports=require(15)
},{}],2000:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":1998,"./lib/window.console.js":1999}],2001:[function(require,module,exports){
module.exports=require(66)
},{}],2002:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":2001}],2003:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":2001}],2004:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":2001}],2005:[function(require,module,exports){
module.exports=require(14)
},{}],2006:[function(require,module,exports){
module.exports=require(15)
},{}],2007:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2005,"./lib/window.console.js":2006}],2008:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2009,"./lib/timers.js":2010}],2009:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":1985,"polyfill/polyfill-base.js":2013}],2010:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2013}],2011:[function(require,module,exports){
module.exports=require(14)
},{}],2012:[function(require,module,exports){
module.exports=require(15)
},{}],2013:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2011,"./lib/window.console.js":2012}],2014:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":2015}],2015:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":2016,"js-ext/lib/object.js":2017}],2016:[function(require,module,exports){
module.exports=require(4)
},{}],2017:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2016,"polyfill/polyfill-base.js":2020,"utils":2021}],2018:[function(require,module,exports){
module.exports=require(14)
},{}],2019:[function(require,module,exports){
module.exports=require(15)
},{}],2020:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2018,"./lib/window.console.js":2019}],2021:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2022,"./lib/timers.js":2023}],2022:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2016,"polyfill/polyfill-base.js":2026}],2023:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2026}],2024:[function(require,module,exports){
module.exports=require(14)
},{}],2025:[function(require,module,exports){
module.exports=require(15)
},{}],2026:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2024,"./lib/window.console.js":2025}],2027:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":1985,"js-ext/lib/object.js":1988,"js-ext/lib/string.js":1990,"polyfill":2007,"polyfill/extra/transition.js":2002,"polyfill/extra/vendorCSS.js":2004}],2028:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":1985,"js-ext/lib/object.js":1988,"polyfill":2007}],2029:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":1985,"js-ext/lib/object.js":1988,"js-ext/lib/string.js":1990,"polyfill":2007}],2030:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":1983,"./attribute-extractor.js":2027,"./element-array.js":2028,"./html-parser.js":2031,"./node-parser.js":2032,"./vdom-ns.js":2033,"./vnode.js":2034,"js-ext/extra/hashmap.js":1985,"js-ext/lib/object.js":1988,"js-ext/lib/promise.js":1989,"js-ext/lib/string.js":1990,"polyfill":2007,"polyfill/extra/transition.js":2002,"polyfill/extra/transitionend.js":2003,"polyfill/extra/vendorCSS.js":2004,"utils":2008,"window-ext":2014}],2031:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":2027,"./vdom-ns.js":2033,"js-ext/extra/hashmap.js":1985,"js-ext/lib/object.js":1988,"polyfill":2007}],2032:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":2027,"./vdom-ns.js":2033,"./vnode.js":2034,"js-ext/extra/hashmap.js":1985,"js-ext/lib/object.js":1988,"polyfill":2007}],2033:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":1985,"js-ext/lib/object.js":1988,"polyfill":2007}],2034:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":2027,"./html-parser.js":2031,"./vdom-ns.js":2033,"js-ext/extra/hashmap.js":1985,"js-ext/extra/lightmap.js":1986,"js-ext/lib/array.js":1987,"js-ext/lib/object.js":1988,"js-ext/lib/string.js":1990,"polyfill":2007,"utils/lib/timers.js":2010}],2035:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":2029,"./partials/extend-element.js":2030,"./partials/node-parser.js":2032,"js-ext/extra/hashmap.js":1985,"js-ext/lib/object.js":1988,"utils/lib/timers.js":2010}],2036:[function(require,module,exports){
module.exports=require(4)
},{}],2037:[function(require,module,exports){
module.exports=require(202)
},{"./lib/array.js":2038,"./lib/function.js":2039,"./lib/json.js":2040,"./lib/object.js":2041,"./lib/promise.js":2042,"./lib/string.js":2043}],2038:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":2046}],2039:[function(require,module,exports){
module.exports=require(204)
},{"polyfill/polyfill-base.js":2046}],2040:[function(require,module,exports){
module.exports=require(205)
},{"polyfill/polyfill-base.js":2046}],2041:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2036,"polyfill/polyfill-base.js":2046,"utils":2047}],2042:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":2046}],2043:[function(require,module,exports){
module.exports=require(29)
},{}],2044:[function(require,module,exports){
module.exports=require(14)
},{}],2045:[function(require,module,exports){
module.exports=require(15)
},{}],2046:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2044,"./lib/window.console.js":2045}],2047:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2048,"./lib/timers.js":2049}],2048:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2036,"polyfill/polyfill-base.js":2052}],2049:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2052}],2050:[function(require,module,exports){
module.exports=require(14)
},{}],2051:[function(require,module,exports){
module.exports=require(15)
},{}],2052:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2050,"./lib/window.console.js":2051}],2053:[function(require,module,exports){
arguments[4][5][0].apply(exports,arguments)
},{"event-dom":2054,"js-ext/extra/classes.js":2149,"js-ext/extra/hashmap.js":2150,"js-ext/lib/object.js":2151,"js-ext/lib/promise.js":2152,"js-ext/lib/string.js":2153,"polyfill":2165,"utils/lib/timers.js":2166,"vdom":2222}],2054:[function(require,module,exports){
"use strict";
/**
* Integrates DOM-events to event. more about DOM-events:
* http://www.smashingmagazine.com/2013/11/12/an-introduction-to-dom-events/
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @example
* Event = require('event-dom')(window);
*
* @module event
* @submodule event-dom
* @class Event
* @since 0.0.1
*/
var NAME = '[event-dom]: ',
Event = require('event'),
later = require('utils').later,
createHashMap = require('js-ext/extra/hashmap.js').createMap,
OUTSIDE = 'outside',
REGEXP_NODE_ID = /^#\S+$/,
REGEXP_EXTRACT_NODE_ID = /#(\S+)/,
REGEXP_UI_OUTSIDE = /^.+outside$/,
TIME_BTN_PRESSED = 200,
PURE_BUTTON_ACTIVE = 'pure-button-active',
UI = 'UI:',
NODE = 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
TAP = 'tap',
CLICK = 'click',
RIGHTCLICK = 'right'+CLICK,
CENTERCLICK = 'center'+CLICK,
EV_REMOVED = UI+NODE+REMOVE,
EV_INSERTED = UI+NODE+INSERT,
EV_CONTENT_CHANGE = UI+NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = UI+ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = UI+ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = UI+ATTRIBUTE+INSERT,
mutationEventsDefined = false,
NO_DEEP_SEARCH = {},
/*
* Internal hash containing all DOM-events that are listened for (at `document`).
*
* DOMEvents = {
* 'tap': callbackFn,
* 'mousemove': callbackFn,
* 'keypress': callbackFn
* }
*
* @property DOMEvents
* @default {}
* @type Object
* @private
* @since 0.0.1
*/
DOMEvents = {};
require('js-ext/lib/string.js');
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('polyfill/polyfill-base.js');
module.exports = function (window) {
var DOCUMENT = window.document,
_domSelToFunc, _evCallback, _findCurrentTargets, _preProcessor, _setupEvents, _setupMutationListener, _teardownMutationListener,
_setupDomListener, _teardownDomListener, SORT, _sortFunc, _sortFuncReversed, _getSubscribers, _selToFunc, MUTATION_EVENTS, preventClick;
require('vdom')(window);
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.EventDom) {
return Event; // Event was already extended
}
console.warn('event-dom 8');
MUTATION_EVENTS = [EV_REMOVED, EV_INSERTED, EV_CONTENT_CHANGE, EV_ATTRIBUTE_REMOVED, , EV_ATTRIBUTE_CHANGED, EV_ATTRIBUTE_INSERTED];
/*
* Transforms the selector to a valid function
*
* @method _selToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_selToFunc = function(customEvent, subscriber) {
Event._sellist.some(function(selFn) {
return selFn(customEvent, subscriber);
});
};
/*
* Creates a filterfunction out of a css-selector. To be used for catching any dom-element, without restrictions
* of any context (like Parcels can --> Parcel.Event uses _parcelSelToDom instead)
* On "non-outside" events, subscriber.t is set to the node that first matches the selector
* so it can be used to set as e.target in the final subscriber
*
* @method _domSelToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_domSelToFunc = function(customEvent, subscriber) {
// this stage is runned during subscription
var outsideEvent = REGEXP_UI_OUTSIDE.test(customEvent),
selector = subscriber.f,
context = subscriber.o,
vnode = subscriber.o.vnode,
isCustomElement = vnode && vnode.isItag,
visibleContent = isCustomElement && !vnode.domNode.contentHidden,
nodeid, byExactId, newTarget, deepSearch;
isCustomElement = false;
console.log(NAME, '_domSelToFunc type of selector = '+typeof selector);
// note: selector could still be a function: in case another subscriber
// already changed it.
if ((!selector && !isCustomElement) || (typeof selector === 'function')) {
subscriber.n || (subscriber.n = isCustomElement ? context : DOCUMENT);
return true;
}
selector || (selector='');
nodeid = selector.match(REGEXP_EXTRACT_NODE_ID);
nodeid ? (subscriber.nId=nodeid[1]) : (subscriber.n=isCustomElement ? context : DOCUMENT);
byExactId = REGEXP_NODE_ID.test(selector);
deepSearch = !NO_DEEP_SEARCH[customEvent];
// set the selector to `subscriber._s` so that e.currentTarget can calculate it:
subscriber._s = selector;
subscriber.f = function(e) {
// this stage is runned when the event happens
console.log(NAME, '_domSelToFunc inside filter. selector: '+selector);
var node = e.target,
vnode = node.vnode,
character1 = selector && selector.substr(1),
match = false;
if (!isCustomElement || visibleContent || context.contains(node)) {
if (selector==='') {
match = true;
}
else {
// e.target is the most deeply node in the dom-tree that caught the event
// our listener uses `selector` which might be a node higher up the tree.
// we will reset e.target to this node (if there is a match)
// note that e.currentTarget will always be `document` --> we're not interested in that
// also, we don't check for `node`, but for node.matchesSelector: the highest level `document`
// is not null, yet it doesn't have .matchesSelector so it would fail
// we DON'T want this for the focus and blur event!
if (deepSearch) {
if (vnode) {
// we go through the vdom
if (!vnode.removedFromDOM) {
while (vnode && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = vnode.domNode;
}
vnode = vnode.vParent;
}
}
}
else {
// we go through the dom
while (node.matchesSelector && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the dom');
match = byExactId ? (node.id===character1) : node.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = node;
}
node = node.parentNode;
}
}
}
else {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
if (vnode) {
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
}
else {
match = node.matchesSelector && (byExactId ? (node.id===character1) : node.matchesSelector(selector));
}
match && (e.sourceTarget=node);
}
}
}
if (outsideEvent && !match) {
// there is a match for the outside-event:
// we need to set e.sourceTarget and e.target:
newTarget = DOCUMENT.getElement(selector, true);
if (newTarget) {
e.sourceTarget = node;
subscriber.t = newTarget;
}
else {
// make return `false` because the selector is not in the dom
match = true;
}
}
console.log(NAME, '_domSelToFunc filter returns '+(!outsideEvent ? match : !match));
return !outsideEvent ? match : !match;
};
return true;
};
// at this point, we need to find out what are the current node-refs. whenever there is
// a filter that starts with `#` --> in those cases we have a bubble-chain, because the selector isn't
// set up with `document` at its root.
// we couldn't do this at time of subscribtion, for the nodes might not be there at that time.
// however, we only need to do this once: we store the value if we find them
// no problem when the nodes leave the dom later: the previous filter wouldn't pass
_findCurrentTargets = function(subscribers) {
console.log(NAME, '_findCurrentTargets');
subscribers.forEach(
function(subscriber) {
console.log(NAME, '_findCurrentTargets for single subscriber. nId: '+subscriber.nId);
subscriber.nId && (subscriber.n=DOCUMENT.getElementById(subscriber.nId, true));
}
);
};
/*
* Generates an event through our Event-system. Does the actual transportation from DOM-events
* into our Eventsystem. It also looks at the response of our Eventsystem: if our system
* halts or preventDefaults the customEvent, then the original DOM-event will be preventDefaulted.
*
* @method _evCallback
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_evCallback = function(e) {
console.log(NAME, '_evCallback');
var allSubscribers = Event._subs,
eType = e.type,
eventobject, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs, subsOutside,
subscribers, eventobjectOutside, wildcard_named_subsOutside, customEvent, eventName, which;
eventName = eType;
// first: a `click` event might be needed to transformed into `rightclick`:
if (eventName===CLICK) {
which = e.which;
(which===2) && (eventName=CENTERCLICK);
(which===3) && (eventName=RIGHTCLICK);
}
if (eventName==='tap') {
// prevent the next click-event
preventClick = true;
e.clientX || (e.clientX = e.center && e.center.x);
e.clientY || (e.clientY = e.center && e.center.y);
}
else if (preventClick && (eventName===CLICK)) {
preventClick = false;
return;
}
if (eventName===CLICK) {
eventName = 'tap';
e.center = {
x: e.clientX,
y: e.clientY
};
e.eventType = 4;
e.pointerType = 'mouse';
e.tapCount = 1;
}
customEvent = 'UI:'+eventName;
subs = allSubscribers[customEvent];
wildcard_named_subs = allSubscribers['*:'+eventName];
named_wildcard_subs = allSubscribers['UI:*'];
wildcard_wildcard_subs = allSubscribers['*:*'];
// Emit the dom-event though our eventsystem:
// NOTE: emit() needs to be synchronous! otherwise we wouldn't be able
// to preventDefault in time
//
// e = eventobject from the DOM-event OR gesture-event
// eventobject = eventobject from our Eventsystem, which get returned by calling `emit()`
// now so the work:
subscribers = _getSubscribers(e, true, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
eventobject = Event._emit(e.target, customEvent, e, subscribers, [], _preProcessor, false, true);
// now check outside subscribers
subsOutside = allSubscribers[customEvent+OUTSIDE];
wildcard_named_subsOutside = allSubscribers['*:'+eventName+OUTSIDE];
subscribers = _getSubscribers(e, true, subsOutside, wildcard_named_subsOutside);
eventobjectOutside = Event._emit(e.target, customEvent+OUTSIDE, e, subscribers, [], _preProcessor, false, true);
// if eventobject was preventdefaulted or halted: take appropriate action on
// the original dom-event. Note: only the original event can caused this, not the outsideevent
// stopPropagation on the original eventobject has no impact on our eventsystem, but who know who else is watching...
// be carefull though: not all gesture events have e.stopPropagation
eventobject.status.halted && e.stopPropagation && e.stopPropagation();
// now we might need to preventDefault the original event.
// be carefull though: not all gesture events have e.preventDefault
if ((eventobject.status.halted || eventobject.status.defaultPrevented || eventobject.status.defaultPreventedContinue) && e.preventDefault) {
e.preventDefault();
}
if (eventobject.status.ok) {
// last step: invoke the aftersubscribers
// we need to do this asynchronous: this way we pass them AFTER the DOM-event's defaultFn
// also make sure to paas-in the payload of the manipulated eventobject
subscribers = _getSubscribers(e, false, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent, eventobject, [], subscribers, _preProcessor, true), 10);
// now check outside subscribers
subscribers = _getSubscribers(e, false, subsOutside, wildcard_named_subsOutside);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent+OUTSIDE, eventobjectOutside, [], subscribers, _preProcessor, true), 10);
}
};
/*
* Creates an array of subscribers in the right order, conform their position in the DOM.
* Only subscribers that match the filter are involved.
*
* @method _getSubscribers
* @param e {Object} eventobject
* @param before {Boolean} whether it is a before- or after-subscriber
* @param subs {Array} array with subscribers
* @param wildcard_named_subs {Array} array with subscribers
* @param named_wildcard_subs {Array} array with subscribers
* @param wildcard_wildcard_subs {Array} array with subscribers
* @private
* @since 0.0.1
*/
_getSubscribers = function(e, before, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs) {
var subscribers = [],
beforeOrAfter = before ? 'b' : 'a',
saveConcat = function(extrasubs) {
extrasubs && extrasubs[beforeOrAfter] && (subscribers=subscribers.concat(extrasubs[beforeOrAfter]));
};
saveConcat(subs);
saveConcat(wildcard_named_subs);
saveConcat(named_wildcard_subs);
saveConcat(wildcard_wildcard_subs);
if (subscribers.length>0) {
subscribers = function(array, testFunc) {
// quickest way to filter an array: see http://jsperf.com/array-filter-performance/4
var filtered = array.slice(0), i;
for (i=array.length-1; i>=0; i--) {
console.log(NAME, 'filtercheck for subscriber');
testFunc(array[i]) || filtered.splice(i, 1);
}
return filtered;
}(subscribers, function(subscriber) {return (!subscriber.f || subscriber.f.call(subscriber.o, e));});
if (subscribers.length>0) {
// _findCurrentTargets(subscribers);
// sorting, based upon the sortFn
subscribers.sort(SORT);
}
}
return subscribers;
};
/*
* Sets e.target and e.sourceTarget for the single subscriber.
* Needs to be done for evenry single subscriber, because with a single event, these values change for each subscriber
*
* @method _preProcessor
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_preProcessor = function(subscriber, e) {
console.log(NAME, '_preProcessor');
// inside the aftersubscribers, we may need exit right away.
// this would be the case whenever stopPropagation or stopImmediatePropagation was called
// in case the subscribernode equals the node on which stopImmediatePropagation was called: return true
var propagationStopped, immediatePropagationStopped,
targetnode = (subscriber.t || subscriber.n);
immediatePropagationStopped = e.status.immediatePropagationStopped;
if (immediatePropagationStopped && ((immediatePropagationStopped===targetnode) || !immediatePropagationStopped.contains(targetnode))) {
console.log(NAME, '_preProcessor will return true because of immediatePropagationStopped');
return true;
}
// in case the subscribernode does not fall within or equals the node on which stopPropagation was called: return true
propagationStopped = e.status.propagationStopped;
if (propagationStopped && (propagationStopped!==targetnode) && !propagationStopped.contains(targetnode)) {
console.log(NAME, '_preProcessor will return true because of propagationStopped');
return true;
}
e._s = subscriber._s;
// now we might need to set e.target to the right node:
// the filterfunction might have found the true domnode that should act as e.target
// and set it at subscriber.t
// also, we need to backup the original e.target: this one should be reset when
// we encounter a subscriber with its own filterfunction instead of selector
if (subscriber.t) {
e.sourceTarget || (e.sourceTarget=e.target);
e.target = subscriber.t;
}
else {
e.sourceTarget && (e.target=e.sourceTarget);
}
return false;
};
/*
* Transports DOM-events to the Event-system. Catches events at their most early stage:
* their capture-phase. When these events happen, a new customEvent is generated by our own
* Eventsystem, by calling _evCallback(). This way we keep DOM-events and our Eventsystem completely separated.
*
* @method _setupDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_setupDomListener = function(customEvent, subscriber) {
console.log(NAME, '_setupDomListener');
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0],
eventName = eventSplitted[1],
outsideEvent = REGEXP_UI_OUTSIDE.test(eventName);
// be careful: anyone could also register an `outside`-event.
// in those cases, the DOM-listener must be set up without `outside`
outsideEvent && (eventName=eventName.substring(0, eventName.length-7));
// if eventName equals `mouseover` or `mouseleave` then we quit:
// people should use `mouseover` and `mouseout`
if ((eventName==='mouseenter') || (eventName==='mouseleave')) {
console.warn(NAME, 'Subscription to '+eventName+' not supported, use mouseover and mouseout: this eventsystem uses these non-noisy so they act as mouseenter and mouseleave');
return;
}
// only accept tap-events, yet later on we WILL need to listen for click-events
(eventName===CLICK) && (eventName=TAP);
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(emitterName+':'+eventName+(outsideEvent ? OUTSIDE : ''), subscriber);
// already registered? then return, also return if someone registered for UI:*
if (DOMEvents[eventName] || (eventName==='*')) {
// cautious: one might have registered the event, but not yet the outsideevent.
// in that case: save this setting:
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
return;
}
DOMEvents[eventName] = true;
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
// one exception: windowresize should listen to the window-object
if (eventName==='resize') {
window.addEventListener(eventName, _evCallback);
}
else {
((eventName===RIGHTCLICK) || (eventName===CENTERCLICK)) && (eventName=TAP);
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.addEventListener(eventName, _evCallback, true);
// listen for both `tap` and `click` events to happen
(eventName===TAP) && DOCUMENT.addEventListener(CLICK, _evCallback, true);
}
};
_setupEvents = function() {
var lastFocussed;
// make sure disabled buttons don't work:
Event.before(['tap', 'press'], function(e) {
e.preventDefault();
}, '.pure-button-disabled, button[disabled]');
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.before(
'keydown',
function(e) {
e._buttonPressed = true;
Event.emit(e.target, 'UI:tap', e);
},
function(e) {
var keyCode = e.keyCode;
return (e.target.getTagName()==='BUTTON') && ((keyCode===13) || (keyCode===32));
}
);
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.after(
'tap',
function(e) {
var buttonNode = e.target;
if (e._buttonPressed) {
buttonNode.setClass(PURE_BUTTON_ACTIVE);
e._noRender = true;
// even if the node isn't in the DOM, we can still try to manipulate it:
// the vdom makes sure no errors occur when the node is already removed
later(buttonNode.removeClass.bind(buttonNode, PURE_BUTTON_ACTIVE), TIME_BTN_PRESSED);
}
}
);
// fix activeElement on Mac
Event.before('focus', function(e) {
// will come here more often because of bubblechain
// however, the last pass-through will set the deepest node
lastFocussed = e.target;
});
// patching DOCUMENT.activeElement because it doesn't work well in a Mac: https://developer.mozilla.org/en-US/docs/Web/API/document.activeElement
// DOCUMENT._activeElement is used with the patch for DOCUMENT.activeElement its getter
Event.after('focus', function() {
DOCUMENT._activeElement = lastFocussed;
});
Event.before('blur', function() {
DOCUMENT._activeElement = null;
});
// Note: window.document has no prototype
Object.defineProperty(DOCUMENT, 'activeElement', {
get: function() {
return DOCUMENT._activeElement || DOCUMENT.body;
}
});
};
_setupMutationListener = function() {
DOCUMENT.hasMutationSubs = true;
if (!mutationEventsDefined) {
Event.defineEvent(EV_REMOVED).unPreventable();
Event.defineEvent(EV_INSERTED).unPreventable();
Event.defineEvent(EV_CONTENT_CHANGE).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_REMOVED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_CHANGED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_INSERTED).unPreventable();
mutationEventsDefined = true;
}
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFunc = function(subscriberOne, subscriberTwo) {
return (subscriberTwo.t || subscriberTwo.n).contains(subscriberOne.t || subscriberOne.n) ? -1 : 1;
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFuncReversed = function(subscriberOne, subscriberTwo) {
return (subscriberOne.t || subscriberOne.n).contains(subscriberTwo.t || subscriberTwo.n) ? 1 : -1;
};
/*
* Removes DOM-eventsubscribers from document when they are no longer needed.
*
* @method _teardownDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @private
* @since 0.0.2
*/
_teardownDomListener = function(customEvent) {
var customEventWithoutOutside = customEvent.endsWith(OUTSIDE) ? customEvent.substr(0, customEvent.length-7) : customEvent,
eventSplitted = customEventWithoutOutside.split(':'),
eventName = eventSplitted[1],
stillInUse;
if ((customEventWithoutOutside===CLICK) || (customEventWithoutOutside===RIGHTCLICK) || (customEventWithoutOutside===CENTERCLICK)) {
stillInUse = Event._subs[CLICK] ||
Event._subs[CLICK+OUTSIDE];
Event._subs[RIGHTCLICK] ||
Event._subs[RIGHTCLICK+OUTSIDE],
Event._subs[CENTERCLICK] ||
Event._subs[CENTERCLICK+OUTSIDE];
eventName = CLICK;
}
else {
stillInUse = Event._subs[customEventWithoutOutside] || Event._subs[customEventWithoutOutside+OUTSIDE];
}
if (!stillInUse) {
console.log(NAME, '_teardownDomListener '+customEvent);
// remove eventlistener from `document`
// one exeption: windowresize should listen to the window-object
if (eventName==='resize') {
window.removeEventListener(eventName, _evCallback);
}
else {
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.removeEventListener(eventName, _evCallback, true);
}
delete DOMEvents[eventName];
}
};
_teardownMutationListener = function() {
if (!Event._subs[EV_REMOVED] &&
!Event._subs[EV_INSERTED] &&
!Event._subs[EV_CONTENT_CHANGE] &&
!Event._subs[EV_ATTRIBUTE_REMOVED] &&
!Event._subs[EV_ATTRIBUTE_CHANGED] &&
!Event._subs[EV_ATTRIBUTE_INSERTED]
) {
DOCUMENT.hasMutationSubs = false;
}
};
// Now a very tricky one:
// Some browsers do an array.sort down-top instead of top-down.
// In those cases we need another sortFn, for the position on an equal match should fall
// behind instead of before (which is the case on top-down sort)
[1,2].sort(function(a /*, b */) {
SORT || (SORT=(a===2) ? _sortFuncReversed : _sortFunc);
});
// Now we do some initialization in order to make DOM-events work:
Object.defineProperty(Event._defaultEventObj, 'currentTarget', {
get: function() {
var ev = this,
e_target = ev.target;
if (e_target && ev._s) {
if (e_target.matches(ev._s)) {
return e_target;
}
return e_target.inside(ev._s) || undefined;
}
}
});
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(UI+'*', _setupDomListener, Event)
._setEventObjProperty('stopPropagation', function() {this.status.ok || (this.status.propagationStopped = this.target);})
._setEventObjProperty('stopImmediatePropagation', function() {this.status.ok || (this.status.immediatePropagationStopped = this.target);});
// Notify when someone subscribes to any event at all --> we might need to transform the filterFn from a selector into a true fnction
// this is already done automaticly by _setupDomListener fo UI:* events
Event.notify('*:*', function(customEvent, subscriber) {
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0];
if ((emitterName!=='UI') && (typeof subscriber.f==='string')) {
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(customEvent, subscriber);
}
}, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(UI+'*', _teardownDomListener, Event);
Event._sellist = [_domSelToFunc];
_setupEvents();
// making HTMLElement to be able to emit using event-emitter:
(function(HTMLElementPrototype) {
HTMLElementPrototype.merge(Event.Emitter('UI'));
}(window.HTMLElement.prototype));
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(MUTATION_EVENTS, _setupMutationListener, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(MUTATION_EVENTS, _teardownMutationListener, Event);
// Note: window.document has no prototype
DOCUMENT.suppressMutationEvents = function(suppress) {
this._suppressMutationEvents = suppress;
};
// Event.noDeepDomEvt and Event._domCallback are the only method that is added to Event.
// We need to do this, because `event-mobile` needs access to the same method.
// We could have done without this method and instead listen for a custom-event to handle
// Mobile events, however, that would lead into 2 eventcycli which isn't performant.
/**
*
* @method noDeepDomEvt
* @param domEvent {String} the eventName that should be processed without deepsearch
* @param e {Object} eventobject
* @for Event
* @chainable
* @since 0.0.1
*/
Event.noDeepDomEvt = function(domEvent) {
domEvent.contains(':') || (domEvent=UI+domEvent);
NO_DEEP_SEARCH[domEvent] = true;
return this;
};
/**
* Does the actual transportation from DOM-events into the Eventsystem. It also looks at the response of
* the Eventsystem: on e.halt() or e.preventDefault(), the original DOM-event will be preventDefaulted.
*
* @method _domCallback
* @param eventName {String} the customEvent that is transported to the eventsystem
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
Event._domCallback = function(e) {
_evCallback(e);
};
// store module:
window._ITSAmodules.EventDom = Event;
return Event;
};
},{"event":2058,"js-ext/extra/hashmap.js":2074,"js-ext/lib/array.js":2075,"js-ext/lib/object.js":2076,"js-ext/lib/string.js":2077,"polyfill/polyfill-base.js":2089,"utils":2090,"vdom":2148}],2055:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":2060,"js-ext/lib/object.js":2061,"polyfill/polyfill-base.js":2073}],2056:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":2055}],2057:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":2055,"js-ext/extra/classes.js":2059,"js-ext/lib/object.js":2061}],2058:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":2055,"./event-emitter.js":2056,"./event-listener.js":2057}],2059:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":2061,"js-ext/extra/hashmap.js":2060,"polyfill/polyfill-base.js":2064}],2060:[function(require,module,exports){
module.exports=require(4)
},{}],2061:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2060,"polyfill/polyfill-base.js":2064,"utils":2065}],2062:[function(require,module,exports){
module.exports=require(14)
},{}],2063:[function(require,module,exports){
module.exports=require(15)
},{}],2064:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2062,"./lib/window.console.js":2063}],2065:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2066,"./lib/timers.js":2067}],2066:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2060,"polyfill/polyfill-base.js":2070}],2067:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2070}],2068:[function(require,module,exports){
module.exports=require(14)
},{}],2069:[function(require,module,exports){
module.exports=require(15)
},{}],2070:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2068,"./lib/window.console.js":2069}],2071:[function(require,module,exports){
module.exports=require(14)
},{}],2072:[function(require,module,exports){
module.exports=require(15)
},{}],2073:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2071,"./lib/window.console.js":2072}],2074:[function(require,module,exports){
module.exports=require(4)
},{}],2075:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":2080}],2076:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2074,"polyfill/polyfill-base.js":2080,"utils":2081}],2077:[function(require,module,exports){
module.exports=require(29)
},{}],2078:[function(require,module,exports){
module.exports=require(14)
},{}],2079:[function(require,module,exports){
module.exports=require(15)
},{}],2080:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2078,"./lib/window.console.js":2079}],2081:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2082,"./lib/timers.js":2083}],2082:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2074,"polyfill/polyfill-base.js":2086}],2083:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2086}],2084:[function(require,module,exports){
module.exports=require(14)
},{}],2085:[function(require,module,exports){
module.exports=require(15)
},{}],2086:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2084,"./lib/window.console.js":2085}],2087:[function(require,module,exports){
module.exports=require(14)
},{}],2088:[function(require,module,exports){
module.exports=require(15)
},{}],2089:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2087,"./lib/window.console.js":2088}],2090:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2091,"./lib/timers.js":2092}],2091:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2074,"polyfill/polyfill-base.js":2095}],2092:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2095}],2093:[function(require,module,exports){
module.exports=require(14)
},{}],2094:[function(require,module,exports){
module.exports=require(15)
},{}],2095:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2093,"./lib/window.console.js":2094}],2096:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],2097:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":2101,"js-ext/extra/hashmap.js":2098,"polyfill/polyfill-base.js":2107}],2098:[function(require,module,exports){
module.exports=require(4)
},{}],2099:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":2100,"../lib/object.js":2101,"./classes.js":2097,"js-ext/extra/hashmap.js":2098,"polyfill/lib/weakmap.js":2105}],2100:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":2107}],2101:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2098,"polyfill/polyfill-base.js":2107,"utils":2108}],2102:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":2107}],2103:[function(require,module,exports){
module.exports=require(29)
},{}],2104:[function(require,module,exports){
module.exports=require(14)
},{}],2105:[function(require,module,exports){
module.exports=require(57)
},{}],2106:[function(require,module,exports){
module.exports=require(15)
},{}],2107:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2104,"./lib/window.console.js":2106}],2108:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2109,"./lib/timers.js":2110}],2109:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2098,"polyfill/polyfill-base.js":2113}],2110:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2113}],2111:[function(require,module,exports){
module.exports=require(14)
},{}],2112:[function(require,module,exports){
module.exports=require(15)
},{}],2113:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2111,"./lib/window.console.js":2112}],2114:[function(require,module,exports){
module.exports=require(66)
},{}],2115:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":2114}],2116:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":2114}],2117:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":2114}],2118:[function(require,module,exports){
module.exports=require(14)
},{}],2119:[function(require,module,exports){
module.exports=require(15)
},{}],2120:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2118,"./lib/window.console.js":2119}],2121:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2122,"./lib/timers.js":2123}],2122:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2098,"polyfill/polyfill-base.js":2126}],2123:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2126}],2124:[function(require,module,exports){
module.exports=require(14)
},{}],2125:[function(require,module,exports){
module.exports=require(15)
},{}],2126:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2124,"./lib/window.console.js":2125}],2127:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":2128}],2128:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":2129,"js-ext/lib/object.js":2130}],2129:[function(require,module,exports){
module.exports=require(4)
},{}],2130:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2129,"polyfill/polyfill-base.js":2133,"utils":2134}],2131:[function(require,module,exports){
module.exports=require(14)
},{}],2132:[function(require,module,exports){
module.exports=require(15)
},{}],2133:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2131,"./lib/window.console.js":2132}],2134:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2135,"./lib/timers.js":2136}],2135:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2129,"polyfill/polyfill-base.js":2139}],2136:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2139}],2137:[function(require,module,exports){
module.exports=require(14)
},{}],2138:[function(require,module,exports){
module.exports=require(15)
},{}],2139:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2137,"./lib/window.console.js":2138}],2140:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":2098,"js-ext/lib/object.js":2101,"js-ext/lib/string.js":2103,"polyfill":2120,"polyfill/extra/transition.js":2115,"polyfill/extra/vendorCSS.js":2117}],2141:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":2098,"js-ext/lib/object.js":2101,"polyfill":2120}],2142:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":2098,"js-ext/lib/object.js":2101,"js-ext/lib/string.js":2103,"polyfill":2120}],2143:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":2096,"./attribute-extractor.js":2140,"./element-array.js":2141,"./html-parser.js":2144,"./node-parser.js":2145,"./vdom-ns.js":2146,"./vnode.js":2147,"js-ext/extra/hashmap.js":2098,"js-ext/lib/object.js":2101,"js-ext/lib/promise.js":2102,"js-ext/lib/string.js":2103,"polyfill":2120,"polyfill/extra/transition.js":2115,"polyfill/extra/transitionend.js":2116,"polyfill/extra/vendorCSS.js":2117,"utils":2121,"window-ext":2127}],2144:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":2140,"./vdom-ns.js":2146,"js-ext/extra/hashmap.js":2098,"js-ext/lib/object.js":2101,"polyfill":2120}],2145:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":2140,"./vdom-ns.js":2146,"./vnode.js":2147,"js-ext/extra/hashmap.js":2098,"js-ext/lib/object.js":2101,"polyfill":2120}],2146:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":2098,"js-ext/lib/object.js":2101,"polyfill":2120}],2147:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":2140,"./html-parser.js":2144,"./vdom-ns.js":2146,"js-ext/extra/hashmap.js":2098,"js-ext/extra/lightmap.js":2099,"js-ext/lib/array.js":2100,"js-ext/lib/object.js":2101,"js-ext/lib/string.js":2103,"polyfill":2120,"utils/lib/timers.js":2123}],2148:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":2142,"./partials/extend-element.js":2143,"./partials/node-parser.js":2145,"js-ext/extra/hashmap.js":2098,"js-ext/lib/object.js":2101,"utils/lib/timers.js":2123}],2149:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":2151,"js-ext/extra/hashmap.js":2150,"polyfill/polyfill-base.js":2156}],2150:[function(require,module,exports){
module.exports=require(4)
},{}],2151:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2150,"polyfill/polyfill-base.js":2156,"utils":2157}],2152:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":2156}],2153:[function(require,module,exports){
module.exports=require(29)
},{}],2154:[function(require,module,exports){
module.exports=require(14)
},{}],2155:[function(require,module,exports){
module.exports=require(15)
},{}],2156:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2154,"./lib/window.console.js":2155}],2157:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2158,"./lib/timers.js":2159}],2158:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2150,"polyfill/polyfill-base.js":2162}],2159:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2162}],2160:[function(require,module,exports){
module.exports=require(14)
},{}],2161:[function(require,module,exports){
module.exports=require(15)
},{}],2162:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2160,"./lib/window.console.js":2161}],2163:[function(require,module,exports){
module.exports=require(14)
},{}],2164:[function(require,module,exports){
module.exports=require(15)
},{}],2165:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2163,"./lib/window.console.js":2164}],2166:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2169}],2167:[function(require,module,exports){
module.exports=require(14)
},{}],2168:[function(require,module,exports){
module.exports=require(15)
},{}],2169:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2167,"./lib/window.console.js":2168}],2170:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],2171:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":2175,"js-ext/extra/hashmap.js":2172,"polyfill/polyfill-base.js":2181}],2172:[function(require,module,exports){
module.exports=require(4)
},{}],2173:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":2174,"../lib/object.js":2175,"./classes.js":2171,"js-ext/extra/hashmap.js":2172,"polyfill/lib/weakmap.js":2179}],2174:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":2181}],2175:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2172,"polyfill/polyfill-base.js":2181,"utils":2182}],2176:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":2181}],2177:[function(require,module,exports){
module.exports=require(29)
},{}],2178:[function(require,module,exports){
module.exports=require(14)
},{}],2179:[function(require,module,exports){
module.exports=require(57)
},{}],2180:[function(require,module,exports){
module.exports=require(15)
},{}],2181:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2178,"./lib/window.console.js":2180}],2182:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2183,"./lib/timers.js":2184}],2183:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2172,"polyfill/polyfill-base.js":2187}],2184:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2187}],2185:[function(require,module,exports){
module.exports=require(14)
},{}],2186:[function(require,module,exports){
module.exports=require(15)
},{}],2187:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2185,"./lib/window.console.js":2186}],2188:[function(require,module,exports){
module.exports=require(66)
},{}],2189:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":2188}],2190:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":2188}],2191:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":2188}],2192:[function(require,module,exports){
module.exports=require(14)
},{}],2193:[function(require,module,exports){
module.exports=require(15)
},{}],2194:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2192,"./lib/window.console.js":2193}],2195:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2196,"./lib/timers.js":2197}],2196:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2172,"polyfill/polyfill-base.js":2200}],2197:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2200}],2198:[function(require,module,exports){
module.exports=require(14)
},{}],2199:[function(require,module,exports){
module.exports=require(15)
},{}],2200:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2198,"./lib/window.console.js":2199}],2201:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":2202}],2202:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":2203,"js-ext/lib/object.js":2204}],2203:[function(require,module,exports){
module.exports=require(4)
},{}],2204:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2203,"polyfill/polyfill-base.js":2207,"utils":2208}],2205:[function(require,module,exports){
module.exports=require(14)
},{}],2206:[function(require,module,exports){
module.exports=require(15)
},{}],2207:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2205,"./lib/window.console.js":2206}],2208:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2209,"./lib/timers.js":2210}],2209:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2203,"polyfill/polyfill-base.js":2213}],2210:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2213}],2211:[function(require,module,exports){
module.exports=require(14)
},{}],2212:[function(require,module,exports){
module.exports=require(15)
},{}],2213:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2211,"./lib/window.console.js":2212}],2214:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":2172,"js-ext/lib/object.js":2175,"js-ext/lib/string.js":2177,"polyfill":2194,"polyfill/extra/transition.js":2189,"polyfill/extra/vendorCSS.js":2191}],2215:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":2172,"js-ext/lib/object.js":2175,"polyfill":2194}],2216:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":2172,"js-ext/lib/object.js":2175,"js-ext/lib/string.js":2177,"polyfill":2194}],2217:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":2170,"./attribute-extractor.js":2214,"./element-array.js":2215,"./html-parser.js":2218,"./node-parser.js":2219,"./vdom-ns.js":2220,"./vnode.js":2221,"js-ext/extra/hashmap.js":2172,"js-ext/lib/object.js":2175,"js-ext/lib/promise.js":2176,"js-ext/lib/string.js":2177,"polyfill":2194,"polyfill/extra/transition.js":2189,"polyfill/extra/transitionend.js":2190,"polyfill/extra/vendorCSS.js":2191,"utils":2195,"window-ext":2201}],2218:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":2214,"./vdom-ns.js":2220,"js-ext/extra/hashmap.js":2172,"js-ext/lib/object.js":2175,"polyfill":2194}],2219:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":2214,"./vdom-ns.js":2220,"./vnode.js":2221,"js-ext/extra/hashmap.js":2172,"js-ext/lib/object.js":2175,"polyfill":2194}],2220:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":2172,"js-ext/lib/object.js":2175,"polyfill":2194}],2221:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":2214,"./html-parser.js":2218,"./vdom-ns.js":2220,"js-ext/extra/hashmap.js":2172,"js-ext/extra/lightmap.js":2173,"js-ext/lib/array.js":2174,"js-ext/lib/object.js":2175,"js-ext/lib/string.js":2177,"polyfill":2194,"utils/lib/timers.js":2197}],2222:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":2216,"./partials/extend-element.js":2217,"./partials/node-parser.js":2219,"js-ext/extra/hashmap.js":2172,"js-ext/lib/object.js":2175,"utils/lib/timers.js":2197}],2223:[function(require,module,exports){
module.exports=require(14)
},{}],2224:[function(require,module,exports){
module.exports=require(15)
},{}],2225:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2223,"./lib/window.console.js":2224}],2226:[function(require,module,exports){
module.exports=require(4)
},{}],2227:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2226,"polyfill/polyfill-base.js":2231,"utils":2232}],2228:[function(require,module,exports){
module.exports=require(29)
},{}],2229:[function(require,module,exports){
module.exports=require(14)
},{}],2230:[function(require,module,exports){
module.exports=require(15)
},{}],2231:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2229,"./lib/window.console.js":2230}],2232:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2233,"./lib/timers.js":2234}],2233:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2226,"polyfill/polyfill-base.js":2237}],2234:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2237}],2235:[function(require,module,exports){
module.exports=require(14)
},{}],2236:[function(require,module,exports){
module.exports=require(15)
},{}],2237:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2235,"./lib/window.console.js":2236}],2238:[function(require,module,exports){
module.exports=require(14)
},{}],2239:[function(require,module,exports){
module.exports=require(15)
},{}],2240:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2238,"./lib/window.console.js":2239}],2241:[function(require,module,exports){
module.exports=require(567)
},{"js-ext/extra/hashmap.js":2226,"js-ext/lib/object.js":2227,"js-ext/lib/string.js":2228,"polyfill":2240}],2242:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],2243:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":2247,"js-ext/extra/hashmap.js":2244,"polyfill/polyfill-base.js":2253}],2244:[function(require,module,exports){
module.exports=require(4)
},{}],2245:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":2246,"../lib/object.js":2247,"./classes.js":2243,"js-ext/extra/hashmap.js":2244,"polyfill/lib/weakmap.js":2251}],2246:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":2253}],2247:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2244,"polyfill/polyfill-base.js":2253,"utils":2254}],2248:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":2253}],2249:[function(require,module,exports){
module.exports=require(29)
},{}],2250:[function(require,module,exports){
module.exports=require(14)
},{}],2251:[function(require,module,exports){
module.exports=require(57)
},{}],2252:[function(require,module,exports){
module.exports=require(15)
},{}],2253:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2250,"./lib/window.console.js":2252}],2254:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2255,"./lib/timers.js":2256}],2255:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2244,"polyfill/polyfill-base.js":2259}],2256:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2259}],2257:[function(require,module,exports){
module.exports=require(14)
},{}],2258:[function(require,module,exports){
module.exports=require(15)
},{}],2259:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2257,"./lib/window.console.js":2258}],2260:[function(require,module,exports){
module.exports=require(66)
},{}],2261:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":2260}],2262:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":2260}],2263:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":2260}],2264:[function(require,module,exports){
module.exports=require(14)
},{}],2265:[function(require,module,exports){
module.exports=require(15)
},{}],2266:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2264,"./lib/window.console.js":2265}],2267:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2268,"./lib/timers.js":2269}],2268:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2244,"polyfill/polyfill-base.js":2272}],2269:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2272}],2270:[function(require,module,exports){
module.exports=require(14)
},{}],2271:[function(require,module,exports){
module.exports=require(15)
},{}],2272:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2270,"./lib/window.console.js":2271}],2273:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":2274}],2274:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":2275,"js-ext/lib/object.js":2276}],2275:[function(require,module,exports){
module.exports=require(4)
},{}],2276:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2275,"polyfill/polyfill-base.js":2279,"utils":2280}],2277:[function(require,module,exports){
module.exports=require(14)
},{}],2278:[function(require,module,exports){
module.exports=require(15)
},{}],2279:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2277,"./lib/window.console.js":2278}],2280:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2281,"./lib/timers.js":2282}],2281:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2275,"polyfill/polyfill-base.js":2285}],2282:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2285}],2283:[function(require,module,exports){
module.exports=require(14)
},{}],2284:[function(require,module,exports){
module.exports=require(15)
},{}],2285:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2283,"./lib/window.console.js":2284}],2286:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":2244,"js-ext/lib/object.js":2247,"js-ext/lib/string.js":2249,"polyfill":2266,"polyfill/extra/transition.js":2261,"polyfill/extra/vendorCSS.js":2263}],2287:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":2244,"js-ext/lib/object.js":2247,"polyfill":2266}],2288:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":2244,"js-ext/lib/object.js":2247,"js-ext/lib/string.js":2249,"polyfill":2266}],2289:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":2242,"./attribute-extractor.js":2286,"./element-array.js":2287,"./html-parser.js":2290,"./node-parser.js":2291,"./vdom-ns.js":2292,"./vnode.js":2293,"js-ext/extra/hashmap.js":2244,"js-ext/lib/object.js":2247,"js-ext/lib/promise.js":2248,"js-ext/lib/string.js":2249,"polyfill":2266,"polyfill/extra/transition.js":2261,"polyfill/extra/transitionend.js":2262,"polyfill/extra/vendorCSS.js":2263,"utils":2267,"window-ext":2273}],2290:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":2286,"./vdom-ns.js":2292,"js-ext/extra/hashmap.js":2244,"js-ext/lib/object.js":2247,"polyfill":2266}],2291:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":2286,"./vdom-ns.js":2292,"./vnode.js":2293,"js-ext/extra/hashmap.js":2244,"js-ext/lib/object.js":2247,"polyfill":2266}],2292:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":2244,"js-ext/lib/object.js":2247,"polyfill":2266}],2293:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":2286,"./html-parser.js":2290,"./vdom-ns.js":2292,"js-ext/extra/hashmap.js":2244,"js-ext/extra/lightmap.js":2245,"js-ext/lib/array.js":2246,"js-ext/lib/object.js":2247,"js-ext/lib/string.js":2249,"polyfill":2266,"utils/lib/timers.js":2269}],2294:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":2288,"./partials/extend-element.js":2289,"./partials/node-parser.js":2291,"js-ext/extra/hashmap.js":2244,"js-ext/lib/object.js":2247,"utils/lib/timers.js":2269}],2295:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":2296}],2296:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":2297,"js-ext/lib/object.js":2298}],2297:[function(require,module,exports){
module.exports=require(4)
},{}],2298:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2297,"polyfill/polyfill-base.js":2301,"utils":2302}],2299:[function(require,module,exports){
module.exports=require(14)
},{}],2300:[function(require,module,exports){
module.exports=require(15)
},{}],2301:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2299,"./lib/window.console.js":2300}],2302:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2303,"./lib/timers.js":2304}],2303:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2297,"polyfill/polyfill-base.js":2307}],2304:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2307}],2305:[function(require,module,exports){
module.exports=require(14)
},{}],2306:[function(require,module,exports){
module.exports=require(15)
},{}],2307:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2305,"./lib/window.console.js":2306}],2308:[function(require,module,exports){
"use strict";
/**
* Integrates DOM-events to event. more about DOM-events:
* http://www.smashingmagazine.com/2013/11/12/an-introduction-to-dom-events/
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @example
* Event = require('event-dom')(window);
*
* @module event
* @submodule event-dom
* @class Event
* @since 0.0.1
*/
var NAME = '[event-dom]: ',
Event = require('event'),
later = require('utils').later,
createHashMap = require('js-ext/extra/hashmap.js').createMap,
OUTSIDE = 'outside',
REGEXP_NODE_ID = /^#\S+$/,
REGEXP_EXTRACT_NODE_ID = /#(\S+)/,
REGEXP_UI_OUTSIDE = /^.+outside$/,
TIME_BTN_PRESSED = 200,
PURE_BUTTON_ACTIVE = 'pure-button-active',
UI = 'UI:',
NODE = 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
TAP = 'tap',
CLICK = 'click',
RIGHTCLICK = 'right'+CLICK,
CENTERCLICK = 'center'+CLICK,
EV_REMOVED = UI+NODE+REMOVE,
EV_INSERTED = UI+NODE+INSERT,
EV_CONTENT_CHANGE = UI+NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = UI+ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = UI+ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = UI+ATTRIBUTE+INSERT,
mutationEventsDefined = false,
NO_DEEP_SEARCH = {},
/*
* Internal hash containing all DOM-events that are listened for (at `document`).
*
* DOMEvents = {
* 'tap': callbackFn,
* 'mousemove': callbackFn,
* 'keypress': callbackFn
* }
*
* @property DOMEvents
* @default {}
* @type Object
* @private
* @since 0.0.1
*/
DOMEvents = {};
require('js-ext/lib/string.js');
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('polyfill/polyfill-base.js');
module.exports = function (window) {
var DOCUMENT = window.document,
_domSelToFunc, _evCallback, _findCurrentTargets, _preProcessor, _setupEvents, _setupMutationListener, _teardownMutationListener,
_setupDomListener, _teardownDomListener, SORT, _sortFunc, _sortFuncReversed, _getSubscribers, _selToFunc, MUTATION_EVENTS, preventClick;
require('vdom')(window);
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.EventDom) {
return Event; // Event was already extended
}
console.warn('event-dom 5');
MUTATION_EVENTS = [EV_REMOVED, EV_INSERTED, EV_CONTENT_CHANGE, EV_ATTRIBUTE_REMOVED, , EV_ATTRIBUTE_CHANGED, EV_ATTRIBUTE_INSERTED];
/*
* Transforms the selector to a valid function
*
* @method _selToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_selToFunc = function(customEvent, subscriber) {
Event._sellist.some(function(selFn) {
return selFn(customEvent, subscriber);
});
};
/*
* Creates a filterfunction out of a css-selector. To be used for catching any dom-element, without restrictions
* of any context (like Parcels can --> Parcel.Event uses _parcelSelToDom instead)
* On "non-outside" events, subscriber.t is set to the node that first matches the selector
* so it can be used to set as e.target in the final subscriber
*
* @method _domSelToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_domSelToFunc = function(customEvent, subscriber) {
// this stage is runned during subscription
var outsideEvent = REGEXP_UI_OUTSIDE.test(customEvent),
selector = subscriber.f,
context = subscriber.o,
vnode = subscriber.o.vnode,
isCustomElement = vnode && vnode.isItag,
visibleContent = isCustomElement && !vnode.domNode.contentHidden,
nodeid, byExactId, newTarget, deepSearch;
isCustomElement = false;
console.log(NAME, '_domSelToFunc type of selector = '+typeof selector);
// note: selector could still be a function: in case another subscriber
// already changed it.
if ((!selector && !isCustomElement) || (typeof selector === 'function')) {
subscriber.n || (subscriber.n = isCustomElement ? context : DOCUMENT);
return true;
}
selector || (selector='');
nodeid = selector.match(REGEXP_EXTRACT_NODE_ID);
nodeid ? (subscriber.nId=nodeid[1]) : (subscriber.n=isCustomElement ? context : DOCUMENT);
byExactId = REGEXP_NODE_ID.test(selector);
deepSearch = !NO_DEEP_SEARCH[customEvent];
// set the selector to `subscriber._s` so that e.currentTarget can calculate it:
subscriber._s = selector;
subscriber.f = function(e) {
// this stage is runned when the event happens
console.log(NAME, '_domSelToFunc inside filter. selector: '+selector);
var node = e.target,
vnode = node.vnode,
character1 = selector && selector.substr(1),
match = false;
if (!isCustomElement || visibleContent || context.contains(node)) {
if (selector==='') {
match = true;
}
else {
// e.target is the most deeply node in the dom-tree that caught the event
// our listener uses `selector` which might be a node higher up the tree.
// we will reset e.target to this node (if there is a match)
// note that e.currentTarget will always be `document` --> we're not interested in that
// also, we don't check for `node`, but for node.matchesSelector: the highest level `document`
// is not null, yet it doesn't have .matchesSelector so it would fail
// we DON'T want this for the focus and blur event!
if (deepSearch) {
if (vnode) {
// we go through the vdom
if (!vnode.removedFromDOM) {
while (vnode && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = vnode.domNode;
}
vnode = vnode.vParent;
}
}
}
else {
// we go through the dom
while (node.matchesSelector && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the dom');
match = byExactId ? (node.id===character1) : node.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = node;
}
node = node.parentNode;
}
}
}
else {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
if (vnode) {
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
}
else {
match = node.matchesSelector && (byExactId ? (node.id===character1) : node.matchesSelector(selector));
}
match && (e.sourceTarget=node);
}
}
}
if (outsideEvent && !match) {
// there is a match for the outside-event:
// we need to set e.sourceTarget and e.target:
newTarget = DOCUMENT.getElement(selector, true);
if (newTarget) {
e.sourceTarget = node;
subscriber.t = newTarget;
}
else {
// make return `false` because the selector is not in the dom
match = true;
}
}
console.log(NAME, '_domSelToFunc filter returns '+(!outsideEvent ? match : !match));
return !outsideEvent ? match : !match;
};
return true;
};
// at this point, we need to find out what are the current node-refs. whenever there is
// a filter that starts with `#` --> in those cases we have a bubble-chain, because the selector isn't
// set up with `document` at its root.
// we couldn't do this at time of subscribtion, for the nodes might not be there at that time.
// however, we only need to do this once: we store the value if we find them
// no problem when the nodes leave the dom later: the previous filter wouldn't pass
_findCurrentTargets = function(subscribers) {
console.log(NAME, '_findCurrentTargets');
subscribers.forEach(
function(subscriber) {
console.log(NAME, '_findCurrentTargets for single subscriber. nId: '+subscriber.nId);
subscriber.nId && (subscriber.n=DOCUMENT.getElementById(subscriber.nId, true));
}
);
};
/*
* Generates an event through our Event-system. Does the actual transportation from DOM-events
* into our Eventsystem. It also looks at the response of our Eventsystem: if our system
* halts or preventDefaults the customEvent, then the original DOM-event will be preventDefaulted.
*
* @method _evCallback
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_evCallback = function(e) {
console.log(NAME, '_evCallback');
var allSubscribers = Event._subs,
eType = e.type,
eventobject, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs, subsOutside,
subscribers, eventobjectOutside, wildcard_named_subsOutside, customEvent, eventName, which;
eventName = eType;
// first: a `click` event might be needed to transformed into `rightclick`:
if (eventName===CLICK) {
which = e.which;
(which===2) && (eventName=CENTERCLICK);
(which===3) && (eventName=RIGHTCLICK);
}
if (eventName==='tap') {
// prevent the next click-event
preventClick = true;
e.clientX || (e.clientX = e.center && e.center.x);
e.clientY || (e.clientY = e.center && e.center.y);
}
else if (preventClick && (eventName===CLICK)) {
preventClick = false;
return;
}
if (eventName===CLICK) {
eventName = 'tap';
e.center = {
x: e.clientX,
y: e.clientY
};
e.eventType = 4;
e.pointerType = 'mouse';
e.tapCount = 1;
}
customEvent = 'UI:'+eventName;
subs = allSubscribers[customEvent];
wildcard_named_subs = allSubscribers['*:'+eventName];
named_wildcard_subs = allSubscribers['UI:*'];
wildcard_wildcard_subs = allSubscribers['*:*'];
// Emit the dom-event though our eventsystem:
// NOTE: emit() needs to be synchronous! otherwise we wouldn't be able
// to preventDefault in time
//
// e = eventobject from the DOM-event OR gesture-event
// eventobject = eventobject from our Eventsystem, which get returned by calling `emit()`
// now so the work:
subscribers = _getSubscribers(e, true, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
eventobject = Event._emit(e.target, customEvent, e, subscribers, [], _preProcessor, false, true);
// now check outside subscribers
subsOutside = allSubscribers[customEvent+OUTSIDE];
wildcard_named_subsOutside = allSubscribers['*:'+eventName+OUTSIDE];
subscribers = _getSubscribers(e, true, subsOutside, wildcard_named_subsOutside);
eventobjectOutside = Event._emit(e.target, customEvent+OUTSIDE, e, subscribers, [], _preProcessor, false, true);
// if eventobject was preventdefaulted or halted: take appropriate action on
// the original dom-event. Note: only the original event can caused this, not the outsideevent
// stopPropagation on the original eventobject has no impact on our eventsystem, but who know who else is watching...
// be carefull though: not all gesture events have e.stopPropagation
eventobject.status.halted && e.stopPropagation && e.stopPropagation();
// now we might need to preventDefault the original event.
// be carefull though: not all gesture events have e.preventDefault
if ((eventobject.status.halted || eventobject.status.defaultPrevented || eventobject.status.defaultPreventedContinue) && e.preventDefault) {
e.preventDefault();
}
if (eventobject.status.ok) {
// last step: invoke the aftersubscribers
// we need to do this asynchronous: this way we pass them AFTER the DOM-event's defaultFn
// also make sure to paas-in the payload of the manipulated eventobject
subscribers = _getSubscribers(e, false, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent, eventobject, [], subscribers, _preProcessor, true), 10);
// now check outside subscribers
subscribers = _getSubscribers(e, false, subsOutside, wildcard_named_subsOutside);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent+OUTSIDE, eventobjectOutside, [], subscribers, _preProcessor, true), 10);
}
};
/*
* Creates an array of subscribers in the right order, conform their position in the DOM.
* Only subscribers that match the filter are involved.
*
* @method _getSubscribers
* @param e {Object} eventobject
* @param before {Boolean} whether it is a before- or after-subscriber
* @param subs {Array} array with subscribers
* @param wildcard_named_subs {Array} array with subscribers
* @param named_wildcard_subs {Array} array with subscribers
* @param wildcard_wildcard_subs {Array} array with subscribers
* @private
* @since 0.0.1
*/
_getSubscribers = function(e, before, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs) {
var subscribers = [],
beforeOrAfter = before ? 'b' : 'a',
saveConcat = function(extrasubs) {
extrasubs && extrasubs[beforeOrAfter] && (subscribers=subscribers.concat(extrasubs[beforeOrAfter]));
};
saveConcat(subs);
saveConcat(wildcard_named_subs);
saveConcat(named_wildcard_subs);
saveConcat(wildcard_wildcard_subs);
if (subscribers.length>0) {
subscribers = function(array, testFunc) {
// quickest way to filter an array: see http://jsperf.com/array-filter-performance/4
var filtered = array.slice(0), i;
for (i=array.length-1; i>=0; i--) {
console.log(NAME, 'filtercheck for subscriber');
testFunc(array[i]) || filtered.splice(i, 1);
}
return filtered;
}(subscribers, function(subscriber) {return (!subscriber.f || subscriber.f.call(subscriber.o, e));});
if (subscribers.length>0) {
// _findCurrentTargets(subscribers);
// sorting, based upon the sortFn
subscribers.sort(SORT);
}
}
return subscribers;
};
/*
* Sets e.target and e.sourceTarget for the single subscriber.
* Needs to be done for evenry single subscriber, because with a single event, these values change for each subscriber
*
* @method _preProcessor
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_preProcessor = function(subscriber, e) {
console.log(NAME, '_preProcessor');
// inside the aftersubscribers, we may need exit right away.
// this would be the case whenever stopPropagation or stopImmediatePropagation was called
// in case the subscribernode equals the node on which stopImmediatePropagation was called: return true
var propagationStopped, immediatePropagationStopped,
targetnode = (subscriber.t || subscriber.n);
immediatePropagationStopped = e.status.immediatePropagationStopped;
if (immediatePropagationStopped && ((immediatePropagationStopped===targetnode) || !immediatePropagationStopped.contains(targetnode))) {
console.log(NAME, '_preProcessor will return true because of immediatePropagationStopped');
return true;
}
// in case the subscribernode does not fall within or equals the node on which stopPropagation was called: return true
propagationStopped = e.status.propagationStopped;
if (propagationStopped && (propagationStopped!==targetnode) && !propagationStopped.contains(targetnode)) {
console.log(NAME, '_preProcessor will return true because of propagationStopped');
return true;
}
e._s = subscriber._s;
// now we might need to set e.target to the right node:
// the filterfunction might have found the true domnode that should act as e.target
// and set it at subscriber.t
// also, we need to backup the original e.target: this one should be reset when
// we encounter a subscriber with its own filterfunction instead of selector
if (subscriber.t) {
e.sourceTarget || (e.sourceTarget=e.target);
e.target = subscriber.t;
}
else {
e.sourceTarget && (e.target=e.sourceTarget);
}
return false;
};
/*
* Transports DOM-events to the Event-system. Catches events at their most early stage:
* their capture-phase. When these events happen, a new customEvent is generated by our own
* Eventsystem, by calling _evCallback(). This way we keep DOM-events and our Eventsystem completely separated.
*
* @method _setupDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_setupDomListener = function(customEvent, subscriber) {
console.log(NAME, '_setupDomListener');
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0],
eventName = eventSplitted[1],
outsideEvent = REGEXP_UI_OUTSIDE.test(eventName);
// be careful: anyone could also register an `outside`-event.
// in those cases, the DOM-listener must be set up without `outside`
outsideEvent && (eventName=eventName.substring(0, eventName.length-7));
// if eventName equals `mouseover` or `mouseleave` then we quit:
// people should use `mouseover` and `mouseout`
if ((eventName==='mouseenter') || (eventName==='mouseleave')) {
console.warn(NAME, 'Subscription to '+eventName+' not supported, use mouseover and mouseout: this eventsystem uses these non-noisy so they act as mouseenter and mouseleave');
return;
}
// only accept tap-events, yet later on we WILL need to listen for click-events
(eventName===CLICK) && (eventName=TAP);
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(emitterName+':'+eventName+(outsideEvent ? OUTSIDE : ''), subscriber);
// already registered? then return, also return if someone registered for UI:*
if (DOMEvents[eventName] || (eventName==='*')) {
// cautious: one might have registered the event, but not yet the outsideevent.
// in that case: save this setting:
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
return;
}
DOMEvents[eventName] = true;
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
// one exception: windowresize should listen to the window-object
if (eventName==='resize') {
window.addEventListener(eventName, _evCallback);
}
else {
((eventName===RIGHTCLICK) || (eventName===CENTERCLICK)) && (eventName=TAP);
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.addEventListener(eventName, _evCallback, true);
// listen for both `tap` and `click` events to happen
(eventName===TAP) && DOCUMENT.addEventListener(CLICK, _evCallback, true);
}
};
_setupEvents = function() {
var lastFocussed;
// make sure disabled buttons don't work:
Event.before(['tap', 'press'], function(e) {
e.preventDefault();
}, '.pure-button-disabled, button[disabled]');
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.before(
'keydown',
function(e) {
e._buttonPressed = true;
Event.emit(e.target, 'UI:tap', e);
},
function(e) {
var keyCode = e.keyCode;
return (e.target.getTagName()==='BUTTON') && ((keyCode===13) || (keyCode===32));
}
);
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.after(
'tap',
function(e) {
var buttonNode = e.target;
if (e._buttonPressed) {
buttonNode.setClass(PURE_BUTTON_ACTIVE);
e._noRender = true;
// even if the node isn't in the DOM, we can still try to manipulate it:
// the vdom makes sure no errors occur when the node is already removed
later(buttonNode.removeClass.bind(buttonNode, PURE_BUTTON_ACTIVE), TIME_BTN_PRESSED);
}
}
);
// fix activeElement on Mac
Event.before('focus', function(e) {
// will come here more often because of bubblechain
// however, the last pass-through will set the deepest node
lastFocussed = e.target;
});
// patching DOCUMENT.activeElement because it doesn't work well in a Mac: https://developer.mozilla.org/en-US/docs/Web/API/document.activeElement
// DOCUMENT._activeElement is used with the patch for DOCUMENT.activeElement its getter
Event.after('focus', function() {
DOCUMENT._activeElement = lastFocussed;
});
Event.before('blur', function() {
DOCUMENT._activeElement = null;
});
// Note: window.document has no prototype
Object.defineProperty(DOCUMENT, 'activeElement', {
get: function() {
return DOCUMENT._activeElement || DOCUMENT.body;
}
});
};
_setupMutationListener = function() {
DOCUMENT.hasMutationSubs = true;
if (!mutationEventsDefined) {
Event.defineEvent(EV_REMOVED).unPreventable();
Event.defineEvent(EV_INSERTED).unPreventable();
Event.defineEvent(EV_CONTENT_CHANGE).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_REMOVED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_CHANGED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_INSERTED).unPreventable();
mutationEventsDefined = true;
}
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFunc = function(subscriberOne, subscriberTwo) {
return (subscriberTwo.t || subscriberTwo.n).contains(subscriberOne.t || subscriberOne.n) ? -1 : 1;
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFuncReversed = function(subscriberOne, subscriberTwo) {
return (subscriberOne.t || subscriberOne.n).contains(subscriberTwo.t || subscriberTwo.n) ? 1 : -1;
};
/*
* Removes DOM-eventsubscribers from document when they are no longer needed.
*
* @method _teardownDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @private
* @since 0.0.2
*/
_teardownDomListener = function(customEvent) {
var customEventWithoutOutside = customEvent.endsWith(OUTSIDE) ? customEvent.substr(0, customEvent.length-7) : customEvent,
eventSplitted = customEventWithoutOutside.split(':'),
eventName = eventSplitted[1],
stillInUse;
if ((customEventWithoutOutside===CLICK) || (customEventWithoutOutside===RIGHTCLICK) || (customEventWithoutOutside===CENTERCLICK)) {
stillInUse = Event._subs[CLICK] ||
Event._subs[CLICK+OUTSIDE];
Event._subs[RIGHTCLICK] ||
Event._subs[RIGHTCLICK+OUTSIDE],
Event._subs[CENTERCLICK] ||
Event._subs[CENTERCLICK+OUTSIDE];
eventName = CLICK;
}
else {
stillInUse = Event._subs[customEventWithoutOutside] || Event._subs[customEventWithoutOutside+OUTSIDE];
}
if (!stillInUse) {
console.log(NAME, '_teardownDomListener '+customEvent);
// remove eventlistener from `document`
// one exeption: windowresize should listen to the window-object
if (eventName==='resize') {
window.removeEventListener(eventName, _evCallback);
}
else {
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.removeEventListener(eventName, _evCallback, true);
}
delete DOMEvents[eventName];
}
};
_teardownMutationListener = function() {
if (!Event._subs[EV_REMOVED] &&
!Event._subs[EV_INSERTED] &&
!Event._subs[EV_CONTENT_CHANGE] &&
!Event._subs[EV_ATTRIBUTE_REMOVED] &&
!Event._subs[EV_ATTRIBUTE_CHANGED] &&
!Event._subs[EV_ATTRIBUTE_INSERTED]
) {
DOCUMENT.hasMutationSubs = false;
}
};
// Now a very tricky one:
// Some browsers do an array.sort down-top instead of top-down.
// In those cases we need another sortFn, for the position on an equal match should fall
// behind instead of before (which is the case on top-down sort)
[1,2].sort(function(a /*, b */) {
SORT || (SORT=(a===2) ? _sortFuncReversed : _sortFunc);
});
// Now we do some initialization in order to make DOM-events work:
Object.defineProperty(Event._defaultEventObj, 'currentTarget', {
get: function() {
var ev = this,
e_target = ev.target;
if (e_target && ev._s) {
if (e_target.matches(ev._s)) {
return e_target;
}
return e_target.inside(ev._s) || undefined;
}
}
});
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(UI+'*', _setupDomListener, Event)
._setEventObjProperty('stopPropagation', function() {this.status.ok || (this.status.propagationStopped = this.target);})
._setEventObjProperty('stopImmediatePropagation', function() {this.status.ok || (this.status.immediatePropagationStopped = this.target);});
// Notify when someone subscribes to any event at all --> we might need to transform the filterFn from a selector into a true fnction
// this is already done automaticly by _setupDomListener fo UI:* events
Event.notify('*:*', function(customEvent, subscriber) {
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0];
if ((emitterName!=='UI') && (typeof subscriber.f==='string')) {
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(customEvent, subscriber);
}
}, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(UI+'*', _teardownDomListener, Event);
Event._sellist = [_domSelToFunc];
_setupEvents();
// making HTMLElement to be able to emit using event-emitter:
(function(HTMLElementPrototype) {
HTMLElementPrototype.merge(Event.Emitter('UI'));
}(window.HTMLElement.prototype));
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(MUTATION_EVENTS, _setupMutationListener, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(MUTATION_EVENTS, _teardownMutationListener, Event);
// Note: window.document has no prototype
DOCUMENT.suppressMutationEvents = function(suppress) {
this._suppressMutationEvents = suppress;
};
// Event.noDeepDomEvt and Event._domCallback are the only method that is added to Event.
// We need to do this, because `event-mobile` needs access to the same method.
// We could have done without this method and instead listen for a custom-event to handle
// Mobile events, however, that would lead into 2 eventcycli which isn't performant.
/**
*
* @method noDeepDomEvt
* @param domEvent {String} the eventName that should be processed without deepsearch
* @param e {Object} eventobject
* @for Event
* @chainable
* @since 0.0.1
*/
Event.noDeepDomEvt = function(domEvent) {
domEvent.contains(':') || (domEvent=UI+domEvent);
NO_DEEP_SEARCH[domEvent] = true;
return this;
};
/**
* Does the actual transportation from DOM-events into the Eventsystem. It also looks at the response of
* the Eventsystem: on e.halt() or e.preventDefault(), the original DOM-event will be preventDefaulted.
*
* @method _domCallback
* @param eventName {String} the customEvent that is transported to the eventsystem
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
Event._domCallback = function(e) {
_evCallback(e);
};
// store module:
window._ITSAmodules.EventDom = Event;
return Event;
};
},{"event":2312,"js-ext/extra/hashmap.js":2328,"js-ext/lib/array.js":2329,"js-ext/lib/object.js":2330,"js-ext/lib/string.js":2331,"polyfill/polyfill-base.js":2343,"utils":2344,"vdom":2402}],2309:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":2314,"js-ext/lib/object.js":2315,"polyfill/polyfill-base.js":2327}],2310:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":2309}],2311:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":2309,"js-ext/extra/classes.js":2313,"js-ext/lib/object.js":2315}],2312:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":2309,"./event-emitter.js":2310,"./event-listener.js":2311}],2313:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":2315,"js-ext/extra/hashmap.js":2314,"polyfill/polyfill-base.js":2318}],2314:[function(require,module,exports){
module.exports=require(4)
},{}],2315:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2314,"polyfill/polyfill-base.js":2318,"utils":2319}],2316:[function(require,module,exports){
module.exports=require(14)
},{}],2317:[function(require,module,exports){
module.exports=require(15)
},{}],2318:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2316,"./lib/window.console.js":2317}],2319:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2320,"./lib/timers.js":2321}],2320:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2314,"polyfill/polyfill-base.js":2324}],2321:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2324}],2322:[function(require,module,exports){
module.exports=require(14)
},{}],2323:[function(require,module,exports){
module.exports=require(15)
},{}],2324:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2322,"./lib/window.console.js":2323}],2325:[function(require,module,exports){
module.exports=require(14)
},{}],2326:[function(require,module,exports){
module.exports=require(15)
},{}],2327:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2325,"./lib/window.console.js":2326}],2328:[function(require,module,exports){
module.exports=require(4)
},{}],2329:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":2334}],2330:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2328,"polyfill/polyfill-base.js":2334,"utils":2335}],2331:[function(require,module,exports){
module.exports=require(29)
},{}],2332:[function(require,module,exports){
module.exports=require(14)
},{}],2333:[function(require,module,exports){
module.exports=require(15)
},{}],2334:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2332,"./lib/window.console.js":2333}],2335:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2336,"./lib/timers.js":2337}],2336:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2328,"polyfill/polyfill-base.js":2340}],2337:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2340}],2338:[function(require,module,exports){
module.exports=require(14)
},{}],2339:[function(require,module,exports){
module.exports=require(15)
},{}],2340:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2338,"./lib/window.console.js":2339}],2341:[function(require,module,exports){
module.exports=require(14)
},{}],2342:[function(require,module,exports){
module.exports=require(15)
},{}],2343:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2341,"./lib/window.console.js":2342}],2344:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2345,"./lib/timers.js":2346}],2345:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2328,"polyfill/polyfill-base.js":2349}],2346:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2349}],2347:[function(require,module,exports){
module.exports=require(14)
},{}],2348:[function(require,module,exports){
module.exports=require(15)
},{}],2349:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2347,"./lib/window.console.js":2348}],2350:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],2351:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":2355,"js-ext/extra/hashmap.js":2352,"polyfill/polyfill-base.js":2361}],2352:[function(require,module,exports){
module.exports=require(4)
},{}],2353:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":2354,"../lib/object.js":2355,"./classes.js":2351,"js-ext/extra/hashmap.js":2352,"polyfill/lib/weakmap.js":2359}],2354:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":2361}],2355:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2352,"polyfill/polyfill-base.js":2361,"utils":2362}],2356:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":2361}],2357:[function(require,module,exports){
module.exports=require(29)
},{}],2358:[function(require,module,exports){
module.exports=require(14)
},{}],2359:[function(require,module,exports){
module.exports=require(57)
},{}],2360:[function(require,module,exports){
module.exports=require(15)
},{}],2361:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2358,"./lib/window.console.js":2360}],2362:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2363,"./lib/timers.js":2364}],2363:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2352,"polyfill/polyfill-base.js":2367}],2364:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2367}],2365:[function(require,module,exports){
module.exports=require(14)
},{}],2366:[function(require,module,exports){
module.exports=require(15)
},{}],2367:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2365,"./lib/window.console.js":2366}],2368:[function(require,module,exports){
module.exports=require(66)
},{}],2369:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":2368}],2370:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":2368}],2371:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":2368}],2372:[function(require,module,exports){
module.exports=require(14)
},{}],2373:[function(require,module,exports){
module.exports=require(15)
},{}],2374:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2372,"./lib/window.console.js":2373}],2375:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2376,"./lib/timers.js":2377}],2376:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2352,"polyfill/polyfill-base.js":2380}],2377:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2380}],2378:[function(require,module,exports){
module.exports=require(14)
},{}],2379:[function(require,module,exports){
module.exports=require(15)
},{}],2380:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2378,"./lib/window.console.js":2379}],2381:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":2382}],2382:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":2383,"js-ext/lib/object.js":2384}],2383:[function(require,module,exports){
module.exports=require(4)
},{}],2384:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2383,"polyfill/polyfill-base.js":2387,"utils":2388}],2385:[function(require,module,exports){
module.exports=require(14)
},{}],2386:[function(require,module,exports){
module.exports=require(15)
},{}],2387:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2385,"./lib/window.console.js":2386}],2388:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2389,"./lib/timers.js":2390}],2389:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2383,"polyfill/polyfill-base.js":2393}],2390:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2393}],2391:[function(require,module,exports){
module.exports=require(14)
},{}],2392:[function(require,module,exports){
module.exports=require(15)
},{}],2393:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2391,"./lib/window.console.js":2392}],2394:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":2352,"js-ext/lib/object.js":2355,"js-ext/lib/string.js":2357,"polyfill":2374,"polyfill/extra/transition.js":2369,"polyfill/extra/vendorCSS.js":2371}],2395:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":2352,"js-ext/lib/object.js":2355,"polyfill":2374}],2396:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":2352,"js-ext/lib/object.js":2355,"js-ext/lib/string.js":2357,"polyfill":2374}],2397:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":2350,"./attribute-extractor.js":2394,"./element-array.js":2395,"./html-parser.js":2398,"./node-parser.js":2399,"./vdom-ns.js":2400,"./vnode.js":2401,"js-ext/extra/hashmap.js":2352,"js-ext/lib/object.js":2355,"js-ext/lib/promise.js":2356,"js-ext/lib/string.js":2357,"polyfill":2374,"polyfill/extra/transition.js":2369,"polyfill/extra/transitionend.js":2370,"polyfill/extra/vendorCSS.js":2371,"utils":2375,"window-ext":2381}],2398:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":2394,"./vdom-ns.js":2400,"js-ext/extra/hashmap.js":2352,"js-ext/lib/object.js":2355,"polyfill":2374}],2399:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":2394,"./vdom-ns.js":2400,"./vnode.js":2401,"js-ext/extra/hashmap.js":2352,"js-ext/lib/object.js":2355,"polyfill":2374}],2400:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":2352,"js-ext/lib/object.js":2355,"polyfill":2374}],2401:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":2394,"./html-parser.js":2398,"./vdom-ns.js":2400,"js-ext/extra/hashmap.js":2352,"js-ext/extra/lightmap.js":2353,"js-ext/lib/array.js":2354,"js-ext/lib/object.js":2355,"js-ext/lib/string.js":2357,"polyfill":2374,"utils/lib/timers.js":2377}],2402:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":2396,"./partials/extend-element.js":2397,"./partials/node-parser.js":2399,"js-ext/extra/hashmap.js":2352,"js-ext/lib/object.js":2355,"utils/lib/timers.js":2377}],2403:[function(require,module,exports){
module.exports=require(4)
},{}],2404:[function(require,module,exports){
module.exports=require(202)
},{"./lib/array.js":2405,"./lib/function.js":2406,"./lib/json.js":2407,"./lib/object.js":2408,"./lib/promise.js":2409,"./lib/string.js":2410}],2405:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":2413}],2406:[function(require,module,exports){
module.exports=require(204)
},{"polyfill/polyfill-base.js":2413}],2407:[function(require,module,exports){
module.exports=require(205)
},{"polyfill/polyfill-base.js":2413}],2408:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2403,"polyfill/polyfill-base.js":2413,"utils":2414}],2409:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":2413}],2410:[function(require,module,exports){
module.exports=require(29)
},{}],2411:[function(require,module,exports){
module.exports=require(14)
},{}],2412:[function(require,module,exports){
module.exports=require(15)
},{}],2413:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2411,"./lib/window.console.js":2412}],2414:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2415,"./lib/timers.js":2416}],2415:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2403,"polyfill/polyfill-base.js":2419}],2416:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2419}],2417:[function(require,module,exports){
module.exports=require(14)
},{}],2418:[function(require,module,exports){
module.exports=require(15)
},{}],2419:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2417,"./lib/window.console.js":2418}],2420:[function(require,module,exports){
arguments[4][5][0].apply(exports,arguments)
},{"event-dom":2421,"js-ext/extra/classes.js":2516,"js-ext/extra/hashmap.js":2517,"js-ext/lib/object.js":2518,"js-ext/lib/promise.js":2519,"js-ext/lib/string.js":2520,"polyfill":2532,"utils/lib/timers.js":2533,"vdom":2589}],2421:[function(require,module,exports){
"use strict";
/**
* Integrates DOM-events to event. more about DOM-events:
* http://www.smashingmagazine.com/2013/11/12/an-introduction-to-dom-events/
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @example
* Event = require('event-dom')(window);
*
* @module event
* @submodule event-dom
* @class Event
* @since 0.0.1
*/
var NAME = '[event-dom]: ',
Event = require('event'),
later = require('utils').later,
createHashMap = require('js-ext/extra/hashmap.js').createMap,
OUTSIDE = 'outside',
REGEXP_NODE_ID = /^#\S+$/,
REGEXP_EXTRACT_NODE_ID = /#(\S+)/,
REGEXP_UI_OUTSIDE = /^.+outside$/,
TIME_BTN_PRESSED = 200,
PURE_BUTTON_ACTIVE = 'pure-button-active',
UI = 'UI:',
NODE = 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
TAP = 'tap',
CLICK = 'click',
RIGHTCLICK = 'right'+CLICK,
CENTERCLICK = 'center'+CLICK,
EV_REMOVED = UI+NODE+REMOVE,
EV_INSERTED = UI+NODE+INSERT,
EV_CONTENT_CHANGE = UI+NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = UI+ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = UI+ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = UI+ATTRIBUTE+INSERT,
mutationEventsDefined = false,
NO_DEEP_SEARCH = {},
/*
* Internal hash containing all DOM-events that are listened for (at `document`).
*
* DOMEvents = {
* 'tap': callbackFn,
* 'mousemove': callbackFn,
* 'keypress': callbackFn
* }
*
* @property DOMEvents
* @default {}
* @type Object
* @private
* @since 0.0.1
*/
DOMEvents = {};
require('js-ext/lib/string.js');
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('polyfill/polyfill-base.js');
module.exports = function (window) {
var DOCUMENT = window.document,
_domSelToFunc, _evCallback, _findCurrentTargets, _preProcessor, _setupEvents, _setupMutationListener, _teardownMutationListener,
_setupDomListener, _teardownDomListener, SORT, _sortFunc, _sortFuncReversed, _getSubscribers, _selToFunc, MUTATION_EVENTS, preventClick;
require('vdom')(window);
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.EventDom) {
return Event; // Event was already extended
}
console.warn('event-dom 4');
MUTATION_EVENTS = [EV_REMOVED, EV_INSERTED, EV_CONTENT_CHANGE, EV_ATTRIBUTE_REMOVED, , EV_ATTRIBUTE_CHANGED, EV_ATTRIBUTE_INSERTED];
/*
* Transforms the selector to a valid function
*
* @method _selToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_selToFunc = function(customEvent, subscriber) {
Event._sellist.some(function(selFn) {
return selFn(customEvent, subscriber);
});
};
/*
* Creates a filterfunction out of a css-selector. To be used for catching any dom-element, without restrictions
* of any context (like Parcels can --> Parcel.Event uses _parcelSelToDom instead)
* On "non-outside" events, subscriber.t is set to the node that first matches the selector
* so it can be used to set as e.target in the final subscriber
*
* @method _domSelToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_domSelToFunc = function(customEvent, subscriber) {
// this stage is runned during subscription
var outsideEvent = REGEXP_UI_OUTSIDE.test(customEvent),
selector = subscriber.f,
context = subscriber.o,
vnode = subscriber.o.vnode,
isCustomElement = vnode && vnode.isItag,
visibleContent = isCustomElement && !vnode.domNode.contentHidden,
nodeid, byExactId, newTarget, deepSearch;
isCustomElement = false;
console.log(NAME, '_domSelToFunc type of selector = '+typeof selector);
// note: selector could still be a function: in case another subscriber
// already changed it.
if ((!selector && !isCustomElement) || (typeof selector === 'function')) {
subscriber.n || (subscriber.n = isCustomElement ? context : DOCUMENT);
return true;
}
selector || (selector='');
nodeid = selector.match(REGEXP_EXTRACT_NODE_ID);
nodeid ? (subscriber.nId=nodeid[1]) : (subscriber.n=isCustomElement ? context : DOCUMENT);
byExactId = REGEXP_NODE_ID.test(selector);
deepSearch = !NO_DEEP_SEARCH[customEvent];
// set the selector to `subscriber._s` so that e.currentTarget can calculate it:
subscriber._s = selector;
subscriber.f = function(e) {
// this stage is runned when the event happens
console.log(NAME, '_domSelToFunc inside filter. selector: '+selector);
var node = e.target,
vnode = node.vnode,
character1 = selector && selector.substr(1),
match = false;
if (!isCustomElement || visibleContent || context.contains(node)) {
if (selector==='') {
match = true;
}
else {
// e.target is the most deeply node in the dom-tree that caught the event
// our listener uses `selector` which might be a node higher up the tree.
// we will reset e.target to this node (if there is a match)
// note that e.currentTarget will always be `document` --> we're not interested in that
// also, we don't check for `node`, but for node.matchesSelector: the highest level `document`
// is not null, yet it doesn't have .matchesSelector so it would fail
// we DON'T want this for the focus and blur event!
if (deepSearch) {
if (vnode) {
// we go through the vdom
if (!vnode.removedFromDOM) {
while (vnode && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = vnode.domNode;
}
vnode = vnode.vParent;
}
}
}
else {
// we go through the dom
while (node.matchesSelector && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the dom');
match = byExactId ? (node.id===character1) : node.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = node;
}
node = node.parentNode;
}
}
}
else {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
if (vnode) {
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
}
else {
match = node.matchesSelector && (byExactId ? (node.id===character1) : node.matchesSelector(selector));
}
match && (e.sourceTarget=node);
}
}
}
if (outsideEvent && !match) {
// there is a match for the outside-event:
// we need to set e.sourceTarget and e.target:
newTarget = DOCUMENT.getElement(selector, true);
if (newTarget) {
e.sourceTarget = node;
subscriber.t = newTarget;
}
else {
// make return `false` because the selector is not in the dom
match = true;
}
}
console.log(NAME, '_domSelToFunc filter returns '+(!outsideEvent ? match : !match));
return !outsideEvent ? match : !match;
};
return true;
};
// at this point, we need to find out what are the current node-refs. whenever there is
// a filter that starts with `#` --> in those cases we have a bubble-chain, because the selector isn't
// set up with `document` at its root.
// we couldn't do this at time of subscribtion, for the nodes might not be there at that time.
// however, we only need to do this once: we store the value if we find them
// no problem when the nodes leave the dom later: the previous filter wouldn't pass
_findCurrentTargets = function(subscribers) {
console.log(NAME, '_findCurrentTargets');
subscribers.forEach(
function(subscriber) {
console.log(NAME, '_findCurrentTargets for single subscriber. nId: '+subscriber.nId);
subscriber.nId && (subscriber.n=DOCUMENT.getElementById(subscriber.nId, true));
}
);
};
/*
* Generates an event through our Event-system. Does the actual transportation from DOM-events
* into our Eventsystem. It also looks at the response of our Eventsystem: if our system
* halts or preventDefaults the customEvent, then the original DOM-event will be preventDefaulted.
*
* @method _evCallback
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_evCallback = function(e) {
console.log(NAME, '_evCallback');
var allSubscribers = Event._subs,
eType = e.type,
eventobject, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs, subsOutside,
subscribers, eventobjectOutside, wildcard_named_subsOutside, customEvent, eventName, which;
eventName = eType;
// first: a `click` event might be needed to transformed into `rightclick`:
if (eventName===CLICK) {
which = e.which;
(which===2) && (eventName=CENTERCLICK);
(which===3) && (eventName=RIGHTCLICK);
}
if (eventName==='tap') {
// prevent the next click-event
preventClick = true;
e.clientX || (e.clientX = e.center && e.center.x);
e.clientY || (e.clientY = e.center && e.center.y);
}
else if (preventClick && (eventName===CLICK)) {
preventClick = false;
return;
}
if (eventName===CLICK) {
eventName = 'tap';
e.center = {
x: e.clientX,
y: e.clientY
};
e.eventType = 4;
e.pointerType = 'mouse';
e.tapCount = 1;
}
customEvent = 'UI:'+eventName;
subs = allSubscribers[customEvent];
wildcard_named_subs = allSubscribers['*:'+eventName];
named_wildcard_subs = allSubscribers['UI:*'];
wildcard_wildcard_subs = allSubscribers['*:*'];
// Emit the dom-event though our eventsystem:
// NOTE: emit() needs to be synchronous! otherwise we wouldn't be able
// to preventDefault in time
//
// e = eventobject from the DOM-event OR gesture-event
// eventobject = eventobject from our Eventsystem, which get returned by calling `emit()`
// now so the work:
subscribers = _getSubscribers(e, true, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
eventobject = Event._emit(e.target, customEvent, e, subscribers, [], _preProcessor, false, true);
// now check outside subscribers
subsOutside = allSubscribers[customEvent+OUTSIDE];
wildcard_named_subsOutside = allSubscribers['*:'+eventName+OUTSIDE];
subscribers = _getSubscribers(e, true, subsOutside, wildcard_named_subsOutside);
eventobjectOutside = Event._emit(e.target, customEvent+OUTSIDE, e, subscribers, [], _preProcessor, false, true);
// if eventobject was preventdefaulted or halted: take appropriate action on
// the original dom-event. Note: only the original event can caused this, not the outsideevent
// stopPropagation on the original eventobject has no impact on our eventsystem, but who know who else is watching...
// be carefull though: not all gesture events have e.stopPropagation
eventobject.status.halted && e.stopPropagation && e.stopPropagation();
// now we might need to preventDefault the original event.
// be carefull though: not all gesture events have e.preventDefault
if ((eventobject.status.halted || eventobject.status.defaultPrevented || eventobject.status.defaultPreventedContinue) && e.preventDefault) {
e.preventDefault();
}
if (eventobject.status.ok) {
// last step: invoke the aftersubscribers
// we need to do this asynchronous: this way we pass them AFTER the DOM-event's defaultFn
// also make sure to paas-in the payload of the manipulated eventobject
subscribers = _getSubscribers(e, false, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent, eventobject, [], subscribers, _preProcessor, true), 10);
// now check outside subscribers
subscribers = _getSubscribers(e, false, subsOutside, wildcard_named_subsOutside);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent+OUTSIDE, eventobjectOutside, [], subscribers, _preProcessor, true), 10);
}
};
/*
* Creates an array of subscribers in the right order, conform their position in the DOM.
* Only subscribers that match the filter are involved.
*
* @method _getSubscribers
* @param e {Object} eventobject
* @param before {Boolean} whether it is a before- or after-subscriber
* @param subs {Array} array with subscribers
* @param wildcard_named_subs {Array} array with subscribers
* @param named_wildcard_subs {Array} array with subscribers
* @param wildcard_wildcard_subs {Array} array with subscribers
* @private
* @since 0.0.1
*/
_getSubscribers = function(e, before, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs) {
var subscribers = [],
beforeOrAfter = before ? 'b' : 'a',
saveConcat = function(extrasubs) {
extrasubs && extrasubs[beforeOrAfter] && (subscribers=subscribers.concat(extrasubs[beforeOrAfter]));
};
saveConcat(subs);
saveConcat(wildcard_named_subs);
saveConcat(named_wildcard_subs);
saveConcat(wildcard_wildcard_subs);
if (subscribers.length>0) {
subscribers = function(array, testFunc) {
// quickest way to filter an array: see http://jsperf.com/array-filter-performance/4
var filtered = array.slice(0), i;
for (i=array.length-1; i>=0; i--) {
console.log(NAME, 'filtercheck for subscriber');
testFunc(array[i]) || filtered.splice(i, 1);
}
return filtered;
}(subscribers, function(subscriber) {return (!subscriber.f || subscriber.f.call(subscriber.o, e));});
if (subscribers.length>0) {
// _findCurrentTargets(subscribers);
// sorting, based upon the sortFn
subscribers.sort(SORT);
}
}
return subscribers;
};
/*
* Sets e.target and e.sourceTarget for the single subscriber.
* Needs to be done for evenry single subscriber, because with a single event, these values change for each subscriber
*
* @method _preProcessor
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_preProcessor = function(subscriber, e) {
console.log(NAME, '_preProcessor');
// inside the aftersubscribers, we may need exit right away.
// this would be the case whenever stopPropagation or stopImmediatePropagation was called
// in case the subscribernode equals the node on which stopImmediatePropagation was called: return true
var propagationStopped, immediatePropagationStopped,
targetnode = (subscriber.t || subscriber.n);
immediatePropagationStopped = e.status.immediatePropagationStopped;
if (immediatePropagationStopped && ((immediatePropagationStopped===targetnode) || !immediatePropagationStopped.contains(targetnode))) {
console.log(NAME, '_preProcessor will return true because of immediatePropagationStopped');
return true;
}
// in case the subscribernode does not fall within or equals the node on which stopPropagation was called: return true
propagationStopped = e.status.propagationStopped;
if (propagationStopped && (propagationStopped!==targetnode) && !propagationStopped.contains(targetnode)) {
console.log(NAME, '_preProcessor will return true because of propagationStopped');
return true;
}
e._s = subscriber._s;
// now we might need to set e.target to the right node:
// the filterfunction might have found the true domnode that should act as e.target
// and set it at subscriber.t
// also, we need to backup the original e.target: this one should be reset when
// we encounter a subscriber with its own filterfunction instead of selector
if (subscriber.t) {
e.sourceTarget || (e.sourceTarget=e.target);
e.target = subscriber.t;
}
else {
e.sourceTarget && (e.target=e.sourceTarget);
}
return false;
};
/*
* Transports DOM-events to the Event-system. Catches events at their most early stage:
* their capture-phase. When these events happen, a new customEvent is generated by our own
* Eventsystem, by calling _evCallback(). This way we keep DOM-events and our Eventsystem completely separated.
*
* @method _setupDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_setupDomListener = function(customEvent, subscriber) {
console.log(NAME, '_setupDomListener');
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0],
eventName = eventSplitted[1],
outsideEvent = REGEXP_UI_OUTSIDE.test(eventName);
// be careful: anyone could also register an `outside`-event.
// in those cases, the DOM-listener must be set up without `outside`
outsideEvent && (eventName=eventName.substring(0, eventName.length-7));
// if eventName equals `mouseover` or `mouseleave` then we quit:
// people should use `mouseover` and `mouseout`
if ((eventName==='mouseenter') || (eventName==='mouseleave')) {
console.warn(NAME, 'Subscription to '+eventName+' not supported, use mouseover and mouseout: this eventsystem uses these non-noisy so they act as mouseenter and mouseleave');
return;
}
// only accept tap-events, yet later on we WILL need to listen for click-events
(eventName===CLICK) && (eventName=TAP);
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(emitterName+':'+eventName+(outsideEvent ? OUTSIDE : ''), subscriber);
// already registered? then return, also return if someone registered for UI:*
if (DOMEvents[eventName] || (eventName==='*')) {
// cautious: one might have registered the event, but not yet the outsideevent.
// in that case: save this setting:
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
return;
}
DOMEvents[eventName] = true;
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
// one exception: windowresize should listen to the window-object
if (eventName==='resize') {
window.addEventListener(eventName, _evCallback);
}
else {
((eventName===RIGHTCLICK) || (eventName===CENTERCLICK)) && (eventName=TAP);
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.addEventListener(eventName, _evCallback, true);
// listen for both `tap` and `click` events to happen
(eventName===TAP) && DOCUMENT.addEventListener(CLICK, _evCallback, true);
}
};
_setupEvents = function() {
var lastFocussed;
// make sure disabled buttons don't work:
Event.before(['tap', 'press'], function(e) {
e.preventDefault();
}, '.pure-button-disabled, button[disabled]');
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.before(
'keydown',
function(e) {
e._buttonPressed = true;
Event.emit(e.target, 'UI:tap', e);
},
function(e) {
var keyCode = e.keyCode;
return (e.target.getTagName()==='BUTTON') && ((keyCode===13) || (keyCode===32));
}
);
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.after(
'tap',
function(e) {
var buttonNode = e.target;
if (e._buttonPressed) {
buttonNode.setClass(PURE_BUTTON_ACTIVE);
e._noRender = true;
// even if the node isn't in the DOM, we can still try to manipulate it:
// the vdom makes sure no errors occur when the node is already removed
later(buttonNode.removeClass.bind(buttonNode, PURE_BUTTON_ACTIVE), TIME_BTN_PRESSED);
}
}
);
// fix activeElement on Mac
Event.before('focus', function(e) {
// will come here more often because of bubblechain
// however, the last pass-through will set the deepest node
lastFocussed = e.target;
});
// patching DOCUMENT.activeElement because it doesn't work well in a Mac: https://developer.mozilla.org/en-US/docs/Web/API/document.activeElement
// DOCUMENT._activeElement is used with the patch for DOCUMENT.activeElement its getter
Event.after('focus', function() {
DOCUMENT._activeElement = lastFocussed;
});
Event.before('blur', function() {
DOCUMENT._activeElement = null;
});
// Note: window.document has no prototype
Object.defineProperty(DOCUMENT, 'activeElement', {
get: function() {
return DOCUMENT._activeElement || DOCUMENT.body;
}
});
};
_setupMutationListener = function() {
DOCUMENT.hasMutationSubs = true;
if (!mutationEventsDefined) {
Event.defineEvent(EV_REMOVED).unPreventable();
Event.defineEvent(EV_INSERTED).unPreventable();
Event.defineEvent(EV_CONTENT_CHANGE).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_REMOVED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_CHANGED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_INSERTED).unPreventable();
mutationEventsDefined = true;
}
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFunc = function(subscriberOne, subscriberTwo) {
return (subscriberTwo.t || subscriberTwo.n).contains(subscriberOne.t || subscriberOne.n) ? -1 : 1;
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFuncReversed = function(subscriberOne, subscriberTwo) {
return (subscriberOne.t || subscriberOne.n).contains(subscriberTwo.t || subscriberTwo.n) ? 1 : -1;
};
/*
* Removes DOM-eventsubscribers from document when they are no longer needed.
*
* @method _teardownDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @private
* @since 0.0.2
*/
_teardownDomListener = function(customEvent) {
var customEventWithoutOutside = customEvent.endsWith(OUTSIDE) ? customEvent.substr(0, customEvent.length-7) : customEvent,
eventSplitted = customEventWithoutOutside.split(':'),
eventName = eventSplitted[1],
stillInUse;
if ((customEventWithoutOutside===CLICK) || (customEventWithoutOutside===RIGHTCLICK) || (customEventWithoutOutside===CENTERCLICK)) {
stillInUse = Event._subs[CLICK] ||
Event._subs[CLICK+OUTSIDE];
Event._subs[RIGHTCLICK] ||
Event._subs[RIGHTCLICK+OUTSIDE],
Event._subs[CENTERCLICK] ||
Event._subs[CENTERCLICK+OUTSIDE];
eventName = CLICK;
}
else {
stillInUse = Event._subs[customEventWithoutOutside] || Event._subs[customEventWithoutOutside+OUTSIDE];
}
if (!stillInUse) {
console.log(NAME, '_teardownDomListener '+customEvent);
// remove eventlistener from `document`
// one exeption: windowresize should listen to the window-object
if (eventName==='resize') {
window.removeEventListener(eventName, _evCallback);
}
else {
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.removeEventListener(eventName, _evCallback, true);
}
delete DOMEvents[eventName];
}
};
_teardownMutationListener = function() {
if (!Event._subs[EV_REMOVED] &&
!Event._subs[EV_INSERTED] &&
!Event._subs[EV_CONTENT_CHANGE] &&
!Event._subs[EV_ATTRIBUTE_REMOVED] &&
!Event._subs[EV_ATTRIBUTE_CHANGED] &&
!Event._subs[EV_ATTRIBUTE_INSERTED]
) {
DOCUMENT.hasMutationSubs = false;
}
};
// Now a very tricky one:
// Some browsers do an array.sort down-top instead of top-down.
// In those cases we need another sortFn, for the position on an equal match should fall
// behind instead of before (which is the case on top-down sort)
[1,2].sort(function(a /*, b */) {
SORT || (SORT=(a===2) ? _sortFuncReversed : _sortFunc);
});
// Now we do some initialization in order to make DOM-events work:
Object.defineProperty(Event._defaultEventObj, 'currentTarget', {
get: function() {
var ev = this,
e_target = ev.target;
if (e_target && ev._s) {
if (e_target.matches(ev._s)) {
return e_target;
}
return e_target.inside(ev._s) || undefined;
}
}
});
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(UI+'*', _setupDomListener, Event)
._setEventObjProperty('stopPropagation', function() {this.status.ok || (this.status.propagationStopped = this.target);})
._setEventObjProperty('stopImmediatePropagation', function() {this.status.ok || (this.status.immediatePropagationStopped = this.target);});
// Notify when someone subscribes to any event at all --> we might need to transform the filterFn from a selector into a true fnction
// this is already done automaticly by _setupDomListener fo UI:* events
Event.notify('*:*', function(customEvent, subscriber) {
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0];
if ((emitterName!=='UI') && (typeof subscriber.f==='string')) {
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(customEvent, subscriber);
}
}, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(UI+'*', _teardownDomListener, Event);
Event._sellist = [_domSelToFunc];
_setupEvents();
// making HTMLElement to be able to emit using event-emitter:
(function(HTMLElementPrototype) {
HTMLElementPrototype.merge(Event.Emitter('UI'));
}(window.HTMLElement.prototype));
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(MUTATION_EVENTS, _setupMutationListener, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(MUTATION_EVENTS, _teardownMutationListener, Event);
// Note: window.document has no prototype
DOCUMENT.suppressMutationEvents = function(suppress) {
this._suppressMutationEvents = suppress;
};
// Event.noDeepDomEvt and Event._domCallback are the only method that is added to Event.
// We need to do this, because `event-mobile` needs access to the same method.
// We could have done without this method and instead listen for a custom-event to handle
// Mobile events, however, that would lead into 2 eventcycli which isn't performant.
/**
*
* @method noDeepDomEvt
* @param domEvent {String} the eventName that should be processed without deepsearch
* @param e {Object} eventobject
* @for Event
* @chainable
* @since 0.0.1
*/
Event.noDeepDomEvt = function(domEvent) {
domEvent.contains(':') || (domEvent=UI+domEvent);
NO_DEEP_SEARCH[domEvent] = true;
return this;
};
/**
* Does the actual transportation from DOM-events into the Eventsystem. It also looks at the response of
* the Eventsystem: on e.halt() or e.preventDefault(), the original DOM-event will be preventDefaulted.
*
* @method _domCallback
* @param eventName {String} the customEvent that is transported to the eventsystem
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
Event._domCallback = function(e) {
_evCallback(e);
};
// store module:
window._ITSAmodules.EventDom = Event;
return Event;
};
},{"event":2425,"js-ext/extra/hashmap.js":2441,"js-ext/lib/array.js":2442,"js-ext/lib/object.js":2443,"js-ext/lib/string.js":2444,"polyfill/polyfill-base.js":2456,"utils":2457,"vdom":2515}],2422:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":2427,"js-ext/lib/object.js":2428,"polyfill/polyfill-base.js":2440}],2423:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":2422}],2424:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":2422,"js-ext/extra/classes.js":2426,"js-ext/lib/object.js":2428}],2425:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":2422,"./event-emitter.js":2423,"./event-listener.js":2424}],2426:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":2428,"js-ext/extra/hashmap.js":2427,"polyfill/polyfill-base.js":2431}],2427:[function(require,module,exports){
module.exports=require(4)
},{}],2428:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2427,"polyfill/polyfill-base.js":2431,"utils":2432}],2429:[function(require,module,exports){
module.exports=require(14)
},{}],2430:[function(require,module,exports){
module.exports=require(15)
},{}],2431:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2429,"./lib/window.console.js":2430}],2432:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2433,"./lib/timers.js":2434}],2433:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2427,"polyfill/polyfill-base.js":2437}],2434:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2437}],2435:[function(require,module,exports){
module.exports=require(14)
},{}],2436:[function(require,module,exports){
module.exports=require(15)
},{}],2437:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2435,"./lib/window.console.js":2436}],2438:[function(require,module,exports){
module.exports=require(14)
},{}],2439:[function(require,module,exports){
module.exports=require(15)
},{}],2440:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2438,"./lib/window.console.js":2439}],2441:[function(require,module,exports){
module.exports=require(4)
},{}],2442:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":2447}],2443:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2441,"polyfill/polyfill-base.js":2447,"utils":2448}],2444:[function(require,module,exports){
module.exports=require(29)
},{}],2445:[function(require,module,exports){
module.exports=require(14)
},{}],2446:[function(require,module,exports){
module.exports=require(15)
},{}],2447:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2445,"./lib/window.console.js":2446}],2448:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2449,"./lib/timers.js":2450}],2449:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2441,"polyfill/polyfill-base.js":2453}],2450:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2453}],2451:[function(require,module,exports){
module.exports=require(14)
},{}],2452:[function(require,module,exports){
module.exports=require(15)
},{}],2453:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2451,"./lib/window.console.js":2452}],2454:[function(require,module,exports){
module.exports=require(14)
},{}],2455:[function(require,module,exports){
module.exports=require(15)
},{}],2456:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2454,"./lib/window.console.js":2455}],2457:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2458,"./lib/timers.js":2459}],2458:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2441,"polyfill/polyfill-base.js":2462}],2459:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2462}],2460:[function(require,module,exports){
module.exports=require(14)
},{}],2461:[function(require,module,exports){
module.exports=require(15)
},{}],2462:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2460,"./lib/window.console.js":2461}],2463:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],2464:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":2468,"js-ext/extra/hashmap.js":2465,"polyfill/polyfill-base.js":2474}],2465:[function(require,module,exports){
module.exports=require(4)
},{}],2466:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":2467,"../lib/object.js":2468,"./classes.js":2464,"js-ext/extra/hashmap.js":2465,"polyfill/lib/weakmap.js":2472}],2467:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":2474}],2468:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2465,"polyfill/polyfill-base.js":2474,"utils":2475}],2469:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":2474}],2470:[function(require,module,exports){
module.exports=require(29)
},{}],2471:[function(require,module,exports){
module.exports=require(14)
},{}],2472:[function(require,module,exports){
module.exports=require(57)
},{}],2473:[function(require,module,exports){
module.exports=require(15)
},{}],2474:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2471,"./lib/window.console.js":2473}],2475:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2476,"./lib/timers.js":2477}],2476:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2465,"polyfill/polyfill-base.js":2480}],2477:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2480}],2478:[function(require,module,exports){
module.exports=require(14)
},{}],2479:[function(require,module,exports){
module.exports=require(15)
},{}],2480:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2478,"./lib/window.console.js":2479}],2481:[function(require,module,exports){
module.exports=require(66)
},{}],2482:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":2481}],2483:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":2481}],2484:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":2481}],2485:[function(require,module,exports){
module.exports=require(14)
},{}],2486:[function(require,module,exports){
module.exports=require(15)
},{}],2487:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2485,"./lib/window.console.js":2486}],2488:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2489,"./lib/timers.js":2490}],2489:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2465,"polyfill/polyfill-base.js":2493}],2490:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2493}],2491:[function(require,module,exports){
module.exports=require(14)
},{}],2492:[function(require,module,exports){
module.exports=require(15)
},{}],2493:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2491,"./lib/window.console.js":2492}],2494:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":2495}],2495:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":2496,"js-ext/lib/object.js":2497}],2496:[function(require,module,exports){
module.exports=require(4)
},{}],2497:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2496,"polyfill/polyfill-base.js":2500,"utils":2501}],2498:[function(require,module,exports){
module.exports=require(14)
},{}],2499:[function(require,module,exports){
module.exports=require(15)
},{}],2500:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2498,"./lib/window.console.js":2499}],2501:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2502,"./lib/timers.js":2503}],2502:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2496,"polyfill/polyfill-base.js":2506}],2503:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2506}],2504:[function(require,module,exports){
module.exports=require(14)
},{}],2505:[function(require,module,exports){
module.exports=require(15)
},{}],2506:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2504,"./lib/window.console.js":2505}],2507:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":2465,"js-ext/lib/object.js":2468,"js-ext/lib/string.js":2470,"polyfill":2487,"polyfill/extra/transition.js":2482,"polyfill/extra/vendorCSS.js":2484}],2508:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":2465,"js-ext/lib/object.js":2468,"polyfill":2487}],2509:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":2465,"js-ext/lib/object.js":2468,"js-ext/lib/string.js":2470,"polyfill":2487}],2510:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":2463,"./attribute-extractor.js":2507,"./element-array.js":2508,"./html-parser.js":2511,"./node-parser.js":2512,"./vdom-ns.js":2513,"./vnode.js":2514,"js-ext/extra/hashmap.js":2465,"js-ext/lib/object.js":2468,"js-ext/lib/promise.js":2469,"js-ext/lib/string.js":2470,"polyfill":2487,"polyfill/extra/transition.js":2482,"polyfill/extra/transitionend.js":2483,"polyfill/extra/vendorCSS.js":2484,"utils":2488,"window-ext":2494}],2511:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":2507,"./vdom-ns.js":2513,"js-ext/extra/hashmap.js":2465,"js-ext/lib/object.js":2468,"polyfill":2487}],2512:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":2507,"./vdom-ns.js":2513,"./vnode.js":2514,"js-ext/extra/hashmap.js":2465,"js-ext/lib/object.js":2468,"polyfill":2487}],2513:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":2465,"js-ext/lib/object.js":2468,"polyfill":2487}],2514:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":2507,"./html-parser.js":2511,"./vdom-ns.js":2513,"js-ext/extra/hashmap.js":2465,"js-ext/extra/lightmap.js":2466,"js-ext/lib/array.js":2467,"js-ext/lib/object.js":2468,"js-ext/lib/string.js":2470,"polyfill":2487,"utils/lib/timers.js":2490}],2515:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":2509,"./partials/extend-element.js":2510,"./partials/node-parser.js":2512,"js-ext/extra/hashmap.js":2465,"js-ext/lib/object.js":2468,"utils/lib/timers.js":2490}],2516:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":2518,"js-ext/extra/hashmap.js":2517,"polyfill/polyfill-base.js":2523}],2517:[function(require,module,exports){
module.exports=require(4)
},{}],2518:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2517,"polyfill/polyfill-base.js":2523,"utils":2524}],2519:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":2523}],2520:[function(require,module,exports){
module.exports=require(29)
},{}],2521:[function(require,module,exports){
module.exports=require(14)
},{}],2522:[function(require,module,exports){
module.exports=require(15)
},{}],2523:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2521,"./lib/window.console.js":2522}],2524:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2525,"./lib/timers.js":2526}],2525:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2517,"polyfill/polyfill-base.js":2529}],2526:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2529}],2527:[function(require,module,exports){
module.exports=require(14)
},{}],2528:[function(require,module,exports){
module.exports=require(15)
},{}],2529:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2527,"./lib/window.console.js":2528}],2530:[function(require,module,exports){
module.exports=require(14)
},{}],2531:[function(require,module,exports){
module.exports=require(15)
},{}],2532:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2530,"./lib/window.console.js":2531}],2533:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2536}],2534:[function(require,module,exports){
module.exports=require(14)
},{}],2535:[function(require,module,exports){
module.exports=require(15)
},{}],2536:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2534,"./lib/window.console.js":2535}],2537:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],2538:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":2542,"js-ext/extra/hashmap.js":2539,"polyfill/polyfill-base.js":2548}],2539:[function(require,module,exports){
module.exports=require(4)
},{}],2540:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":2541,"../lib/object.js":2542,"./classes.js":2538,"js-ext/extra/hashmap.js":2539,"polyfill/lib/weakmap.js":2546}],2541:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":2548}],2542:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2539,"polyfill/polyfill-base.js":2548,"utils":2549}],2543:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":2548}],2544:[function(require,module,exports){
module.exports=require(29)
},{}],2545:[function(require,module,exports){
module.exports=require(14)
},{}],2546:[function(require,module,exports){
module.exports=require(57)
},{}],2547:[function(require,module,exports){
module.exports=require(15)
},{}],2548:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2545,"./lib/window.console.js":2547}],2549:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2550,"./lib/timers.js":2551}],2550:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2539,"polyfill/polyfill-base.js":2554}],2551:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2554}],2552:[function(require,module,exports){
module.exports=require(14)
},{}],2553:[function(require,module,exports){
module.exports=require(15)
},{}],2554:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2552,"./lib/window.console.js":2553}],2555:[function(require,module,exports){
module.exports=require(66)
},{}],2556:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":2555}],2557:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":2555}],2558:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":2555}],2559:[function(require,module,exports){
module.exports=require(14)
},{}],2560:[function(require,module,exports){
module.exports=require(15)
},{}],2561:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2559,"./lib/window.console.js":2560}],2562:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2563,"./lib/timers.js":2564}],2563:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2539,"polyfill/polyfill-base.js":2567}],2564:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2567}],2565:[function(require,module,exports){
module.exports=require(14)
},{}],2566:[function(require,module,exports){
module.exports=require(15)
},{}],2567:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2565,"./lib/window.console.js":2566}],2568:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":2569}],2569:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":2570,"js-ext/lib/object.js":2571}],2570:[function(require,module,exports){
module.exports=require(4)
},{}],2571:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2570,"polyfill/polyfill-base.js":2574,"utils":2575}],2572:[function(require,module,exports){
module.exports=require(14)
},{}],2573:[function(require,module,exports){
module.exports=require(15)
},{}],2574:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2572,"./lib/window.console.js":2573}],2575:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2576,"./lib/timers.js":2577}],2576:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2570,"polyfill/polyfill-base.js":2580}],2577:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2580}],2578:[function(require,module,exports){
module.exports=require(14)
},{}],2579:[function(require,module,exports){
module.exports=require(15)
},{}],2580:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2578,"./lib/window.console.js":2579}],2581:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":2539,"js-ext/lib/object.js":2542,"js-ext/lib/string.js":2544,"polyfill":2561,"polyfill/extra/transition.js":2556,"polyfill/extra/vendorCSS.js":2558}],2582:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":2539,"js-ext/lib/object.js":2542,"polyfill":2561}],2583:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":2539,"js-ext/lib/object.js":2542,"js-ext/lib/string.js":2544,"polyfill":2561}],2584:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":2537,"./attribute-extractor.js":2581,"./element-array.js":2582,"./html-parser.js":2585,"./node-parser.js":2586,"./vdom-ns.js":2587,"./vnode.js":2588,"js-ext/extra/hashmap.js":2539,"js-ext/lib/object.js":2542,"js-ext/lib/promise.js":2543,"js-ext/lib/string.js":2544,"polyfill":2561,"polyfill/extra/transition.js":2556,"polyfill/extra/transitionend.js":2557,"polyfill/extra/vendorCSS.js":2558,"utils":2562,"window-ext":2568}],2585:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":2581,"./vdom-ns.js":2587,"js-ext/extra/hashmap.js":2539,"js-ext/lib/object.js":2542,"polyfill":2561}],2586:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":2581,"./vdom-ns.js":2587,"./vnode.js":2588,"js-ext/extra/hashmap.js":2539,"js-ext/lib/object.js":2542,"polyfill":2561}],2587:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":2539,"js-ext/lib/object.js":2542,"polyfill":2561}],2588:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":2581,"./html-parser.js":2585,"./vdom-ns.js":2587,"js-ext/extra/hashmap.js":2539,"js-ext/extra/lightmap.js":2540,"js-ext/lib/array.js":2541,"js-ext/lib/object.js":2542,"js-ext/lib/string.js":2544,"polyfill":2561,"utils/lib/timers.js":2564}],2589:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":2583,"./partials/extend-element.js":2584,"./partials/node-parser.js":2586,"js-ext/extra/hashmap.js":2539,"js-ext/lib/object.js":2542,"utils/lib/timers.js":2564}],2590:[function(require,module,exports){
module.exports=require(14)
},{}],2591:[function(require,module,exports){
module.exports=require(15)
},{}],2592:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2590,"./lib/window.console.js":2591}],2593:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],2594:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":2598,"js-ext/extra/hashmap.js":2595,"polyfill/polyfill-base.js":2604}],2595:[function(require,module,exports){
module.exports=require(4)
},{}],2596:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":2597,"../lib/object.js":2598,"./classes.js":2594,"js-ext/extra/hashmap.js":2595,"polyfill/lib/weakmap.js":2602}],2597:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":2604}],2598:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2595,"polyfill/polyfill-base.js":2604,"utils":2605}],2599:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":2604}],2600:[function(require,module,exports){
module.exports=require(29)
},{}],2601:[function(require,module,exports){
module.exports=require(14)
},{}],2602:[function(require,module,exports){
module.exports=require(57)
},{}],2603:[function(require,module,exports){
module.exports=require(15)
},{}],2604:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2601,"./lib/window.console.js":2603}],2605:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2606,"./lib/timers.js":2607}],2606:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2595,"polyfill/polyfill-base.js":2610}],2607:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2610}],2608:[function(require,module,exports){
module.exports=require(14)
},{}],2609:[function(require,module,exports){
module.exports=require(15)
},{}],2610:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2608,"./lib/window.console.js":2609}],2611:[function(require,module,exports){
module.exports=require(66)
},{}],2612:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":2611}],2613:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":2611}],2614:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":2611}],2615:[function(require,module,exports){
module.exports=require(14)
},{}],2616:[function(require,module,exports){
module.exports=require(15)
},{}],2617:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2615,"./lib/window.console.js":2616}],2618:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2619,"./lib/timers.js":2620}],2619:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2595,"polyfill/polyfill-base.js":2623}],2620:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2623}],2621:[function(require,module,exports){
module.exports=require(14)
},{}],2622:[function(require,module,exports){
module.exports=require(15)
},{}],2623:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2621,"./lib/window.console.js":2622}],2624:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":2625}],2625:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":2626,"js-ext/lib/object.js":2627}],2626:[function(require,module,exports){
module.exports=require(4)
},{}],2627:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2626,"polyfill/polyfill-base.js":2630,"utils":2631}],2628:[function(require,module,exports){
module.exports=require(14)
},{}],2629:[function(require,module,exports){
module.exports=require(15)
},{}],2630:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2628,"./lib/window.console.js":2629}],2631:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2632,"./lib/timers.js":2633}],2632:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2626,"polyfill/polyfill-base.js":2636}],2633:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2636}],2634:[function(require,module,exports){
module.exports=require(14)
},{}],2635:[function(require,module,exports){
module.exports=require(15)
},{}],2636:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2634,"./lib/window.console.js":2635}],2637:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":2595,"js-ext/lib/object.js":2598,"js-ext/lib/string.js":2600,"polyfill":2617,"polyfill/extra/transition.js":2612,"polyfill/extra/vendorCSS.js":2614}],2638:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":2595,"js-ext/lib/object.js":2598,"polyfill":2617}],2639:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":2595,"js-ext/lib/object.js":2598,"js-ext/lib/string.js":2600,"polyfill":2617}],2640:[function(require,module,exports){
arguments[4][95][0].apply(exports,arguments)
},{"../css/element.css":2593,"./attribute-extractor.js":2637,"./element-array.js":2638,"./html-parser.js":2641,"./node-parser.js":2642,"./vdom-ns.js":2643,"./vnode.js":2644,"js-ext/extra/hashmap.js":2595,"js-ext/lib/object.js":2598,"js-ext/lib/promise.js":2599,"js-ext/lib/string.js":2600,"polyfill":2617,"polyfill/extra/transition.js":2612,"polyfill/extra/transitionend.js":2613,"polyfill/extra/vendorCSS.js":2614,"utils":2618,"window-ext":2624}],2641:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":2637,"./vdom-ns.js":2643,"js-ext/extra/hashmap.js":2595,"js-ext/lib/object.js":2598,"polyfill":2617}],2642:[function(require,module,exports){
arguments[4][97][0].apply(exports,arguments)
},{"./attribute-extractor.js":2637,"./vdom-ns.js":2643,"./vnode.js":2644,"js-ext/extra/hashmap.js":2595,"js-ext/lib/object.js":2598,"polyfill":2617}],2643:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":2595,"js-ext/lib/object.js":2598,"polyfill":2617}],2644:[function(require,module,exports){
"use strict";
/**
* Delivers the `vnode` prototype object, which is a virtualisation of an `Element` inside the Dom.
* These Elements work smoothless with the vdom (see ...).
*
* vnodes are much quicker to access and walk through than native dom-nodes. However, this is a module you don't need
* by itself: `Element`-types use these features under the hood.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* <br>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
*
* @module vdom
* @submodule vnode
* @class vnode
* @since 0.0.1
*/
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('js-ext/lib/string.js');
require('polyfill');
var createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.VNode) {
return window._ITSAmodules.VNode; // VNODE was already created
}
console.warn('VDOM 32');
var NS = require('./vdom-ns.js')(window),
extractor = require('./attribute-extractor.js')(window),
DOCUMENT = window.document,
LightMap = require('js-ext/extra/lightmap.js'),
MUTATION_EVENTS = new LightMap(),
BATCH_WILL_RUN = false,
xmlNS = NS.xmlNS,
nodeids = NS.nodeids,
htmlToVNodes = require('./html-parser.js')(window),
timers = require('utils/lib/timers.js'),
async = timers.async,
later = timers.later,
/*jshint proto:true */
PROTO_SUPPORTED = !!Object.__proto__,
/*jshint proto:false */
// cleanup memory after 1 minute: removed nodes SHOULD NOT be accessed afterwards
// because vnode would be recalculated and might be different from before
DESTROY_DELAY = 60000,
unescapeEntities = NS.UnescapeEntities,
NTH_CHILD_REGEXP = /^(?:(\d*)[n|N])([\+|\-](\d+))?$/, // an+b
STRING = 'string',
CLASS = 'class',
STYLE = 'style',
ID = 'id',
NODE= 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
EV_REMOVED = NODE+REMOVE,
EV_INSERTED = NODE+INSERT,
EV_CONTENT_CHANGE = NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = ATTRIBUTE+INSERT,
SPLIT_CHARACTER = createHashMap({
' ': true,
'>': true,
'+': true, // only select the element when it is immediately preceded by the former element
'~': true // only the element when it has the former element as a sibling. (just like `+`, but less strict)
}),
STORABLE_SPLIT_CHARACTER = createHashMap({
'>': true,
'+': true,
'~': true
}),
SIBLING_MATCH_CHARACTER = createHashMap({
'+': true,
'~': true
}),
ATTR_DETAIL_SPECIFIERS = createHashMap({
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to attribute-name end-tokens.
*
* @property END_ATTRIBUTENAME
* @default {
* '=': true,
* ']': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
END_ATTRIBUTENAME = createHashMap({
'=': true,
']': true,
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to different changes of Element nodeType changes.
*
* @property NODESWITCH
* @default {
* 1: {
* 1: 1,
* 3: 2,
* 8: 3
* },
* 3: {
* 1: 4,
* 3: 5,
* 8: 6
* },
* 8: {
* 1: 7,
* 3: 8,
* 8: 9
* }
* }
* @type Object
* @protected
* @since 0.0.1
*/
NODESWITCH = createHashMap({
1: createHashMap({
1: 1, // oldNodeType==Element, newNodeType==Element
3: 2, // oldNodeType==Element, newNodeType==TextNode
8: 3 // oldNodeType==Element, newNodeType==Comment
}),
3: createHashMap({
1: 4, // oldNodeType==TextNode, newNodeType==Element
3: 5, // oldNodeType==TextNode, newNodeType==TextNode
8: 6 // oldNodeType==TextNode, newNodeType==Comment
}),
8: createHashMap({
1: 7, // oldNodeType==Comment, newNodeType==Element
3: 8, // oldNodeType==Comment, newNodeType==TextNode
8: 9 // oldNodeType==Comment, newNodeType==Comment
})
}),
/**
* Object to gain quick access to selector start-tokens.
*
* @property SELECTOR_IDENTIFIERS
* @default {
* '#': 1,
* '.': 2,
* '[': 3
* }
* @type Object
* @protected
* @since 0.0.1
*/
SELECTOR_IDENTIFIERS = createHashMap({
'#': 1,
'.': 2,
'[': 3,
':': 4
}),
PSEUDO_FIRST_CHILD = ':first-child',
PSEUDO_FIRST_OF_TYPE = ':first-of-type',
PSEUDO_LAST_CHILD = ':last-child',
PSEUDO_LAST_OF_TYPE = ':last-of-type',
PSEUDO_NTH_CHILD = ':nth-child',
PSEUDO_NTH_LAST_CHILD = ':nth-last-child',
PSEUDO_NTH_LAST_OF_TYPE = ':nth-last-of-type',
PSEUDO_NTH_OF_TYPE = ':nth-of-type',
PSEUDO_ONLY_OF_TYPE = ':only-of-type',
PSEUDO_ONLY_CHILD = ':only-child',
/**
* Object to gain quick access to the selectors that required children
*
* @property PSEUDO_REQUIRED_CHILDREN
* @default {
* ':first-child': true,
* ':first-of-type': true,
* ':last-child': true,
* ':last-of-type': true,
* ':nth-child': true,
* ':nth-last-child': true,
* ':nth-last-of-type': true,
* ':nth-of-type': true,
* ':only-of-type': true,
* ':only-child': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
PSEUDO_REQUIRED_CHILDREN = createHashMap(),
_matchesSelectorItem, _matchesOneSelector, _findElementSibling, vNodeProto, _markRemoved, _tryReplaceChild,
_splitSelector, _findNodeSibling, _matchNthChild, _batchEmit, _emitDestroyChildren, _tryRemoveDomNode;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_CHILD] = true;
/**
* Searches for the next -or previous- node-sibling (nodeType of 1, 3 or 8).
*
* @method _findNodeSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findNodeSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
index = vParent.vChildNodes.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildNodes[index];
};
/**
* Searches for the next -or previous- Element-sibling (nodeType of 1).
*
* @method _findElementSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findElementSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
if (vnode.nodeType===1) {
index = vParent.vChildren.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildren[index];
}
else {
/*jshint noempty:true */
while ((vnode=_findNodeSibling(vnode, next)) && (vnode.nodeType!==1)) {}
/*jshint noempty:false */
return vnode;
}
};
/**
* Check whether the vnode matches a "nth-child" test, which is used for css pseudoselectors like `nth-child`, `nth-of-type` etc.
*
* @method _matchNthChild
* @param pseudoArg {String} the argument for nth-child
* @param index {Number} the index of the inspected vnode
* @return {Boolean} whether the vnode matches the nthChild test
* @protected
* @private
* @since 0.0.1
*/
_matchNthChild = function(pseudoArg, index) {
var match, k, a, b, nodeOk, nthIndex, sign, isNumber;
(pseudoArg==='even') && (pseudoArg='2n');
(pseudoArg==='odd') && (pseudoArg='2n+1');
match = pseudoArg.match(NTH_CHILD_REGEXP) || (isNumber=pseudoArg.validateNumber());
if (!match) {
return false;
}
// pseudoArg follows the pattern: `an+b`
if (!isNumber) {
a = match[1];
sign = match[2];
b = match[3] || 0;
(b==='') && (b=0);
}
else {
b = pseudoArg;
}
sign && (sign=sign[0]);
if (!a) {
// only fixed index to match
return (sign==='-') ? false : (parseInt(b, 10)===index);
}
else {
// we need to iterate
nodeOk = false;
b = window.Number(b);
for (k=0; !nodeOk; k++) {
nthIndex = (sign==='-') ? (a*k) - b : (a*k) + b;
if (nthIndex===index) {
nodeOk = true;
}
else if (nthIndex>index) {
// beyond index --> will never become a fix anymore
return false;
}
}
return nodeOk;
}
};
/**
* Check whether the vnode matches the css-selector. the css-selector should be a single selector,
* not multiple, so it shouldn't contain a `comma`.
*
* @method _matchesOneSelector
* @param vnode {vnode} the vnode to inspect
* @param selector {String} the selector-item to check the match for
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches the css-selector
* @protected
* @private
* @since 0.0.1
*/
_matchesOneSelector = function(vnode, selector, relatedVNode) {
var selList = _splitSelector(selector),
size = selList.length,
selMatch = false,
i, selectorItem, last,
rightvnode, relationMatch, checkRelation;
if (STORABLE_SPLIT_CHARACTER[selList[size-1]]) {
return false;
}
relationMatch = function(leftVNode, rightVNode, relationCharacter) {
var match, vParent, vChildren;
// when `selector` starts with `>`, `~` or `+`, then
// there should also be a match comparing a related node!
switch (relationCharacter) {
case '>':
leftVNode || (leftVNode=rightVNode.vParent);
match = (leftVNode.vChildren.indexOf(rightVNode)!==-1);
break;
case '~':
leftVNode || (leftVNode=rightVNode.vFirstElement);
vParent = leftVNode.vParent;
vChildren = vParent && vParent.vChildren;
match = vParent && (vChildren.indexOf(leftVNode)<vChildren.indexOf(rightVNode));
break;
case '+':
leftVNode || (leftVNode=rightVNode.vPreviousElement);
match = (leftVNode.vNextElement === rightVNode);
}
return !!match;
};
last = size - 1;
i = last;
while (vnode && ((selMatch || (i===last)) && (i>=0))) {
selectorItem = selList[i];
if (STORABLE_SPLIT_CHARACTER[selectorItem]) {
checkRelation = selectorItem;
i--;
}
else {
selMatch = _matchesSelectorItem(vnode, selectorItem);
if (!selMatch && (i===last)) {
return false;
}
while (!selMatch && vnode && (i!==last)) {
// may also look at nodes higher in the chain
vnode = SIBLING_MATCH_CHARACTER[selList[i+1]] ? vnode.vPreviousElement : vnode.vParent;
selMatch = vnode && _matchesSelectorItem(vnode, selectorItem);
}
selMatch && checkRelation && vnode && (selMatch=relationMatch(vnode, rightvnode, checkRelation));
rightvnode = vnode;
if (vnode) {
// do a precheck on the next checkRelation:
// and determine whether to set the vnode upper the tree or at the previous sibling:
vnode = SIBLING_MATCH_CHARACTER[selList[i-1]] ? vnode.vPreviousElement : vnode.vParent;
}
if (selMatch) {
checkRelation = false;
i--;
}
}
}
if ((rightvnode===relatedVNode) || (i!==-1)) {
selMatch = false;
}
selMatch && checkRelation && rightvnode && (selMatch=relationMatch(relatedVNode, rightvnode, checkRelation));
return selMatch;
};
/**
* Check whether the vnode matches one specific selector-item. Suppose the css-selector: "#mynode li.red .blue"
* then there are 3 selector-items: "#mynode", "li.red" and ".blue"
*
* This method also can handle the new selectors:
* <ul>
* <li>[att^=val] –-> the “begins with” selector</li>
* <li>[att$=val] –-> the “ends with” selector</li>
* <li>[att*=val] –-> the “contains” selector (might be a substring)</li>
* <li>[att~=val] –-> the “contains” selector as a separate word, separated by spaces</li>
* <li>[att|=val] –-> the “contains” selector as a separate word, separated by `|`</li>
* <li>+ --> (same level)</li>
* <li>~ --> (same level)</li>
* </ul>
*
* @method _matchesSelectorItem
* @param vnode {Object} the vnode to inspect
* @param selectorItem {String} the selector-item to check the match for
* @return {Boolean} whether the vnode matches the selector-item
* @protected
* @private
* @since 0.0.1
*/
_matchesSelectorItem = function (vnode, selectorItem) {
var i = 0,
len = selectorItem.length,
character = selectorItem[0],
tagName, id, className, attributeName, attributeValue, stringMarker, attributeisString, isBoolean, insideAttributeValue, insideAttribute,
vParent, checkBoolean, treatment, k, min, max, value, len2, index, found, pseudo, pseudoArg, arglevel, count, vParentVChildren;
if (selectorItem==='*') {
return true;
}
if (!SELECTOR_IDENTIFIERS[character]) {
// starts with tagName
tagName = '';
// reposition i to continue in the right way:
i--;
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
tagName += character;
}
if (tagName.toUpperCase()!==vnode.tag) {
return false;
}
}
while (i<len) {
switch (character) {
case '#':
id = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
id += character;
}
if (id!==vnode.id) {
return false;
}
break;
case '.':
className = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
className += character;
}
if (!vnode.hasClass(className)) {
return false;
}
break;
case '[':
attributeName = '';
while ((++i<len) && (character=selectorItem[i]) && !END_ATTRIBUTENAME[character]) {
attributeName += character;
}
// if character===']' then we have an attribute without a value-definition
if (!vnode.attrs[attributeName] || ((character===']') && (vnode.attrs[attributeName]!==''))) {
return !!vnode.attrs[attributeName];
}
// now we read the value of the attribute
// however, it could be that the selector has a special `detailed` identifier set (defined by: ATTR_DETAIL_SPECIFIERS)
if (ATTR_DETAIL_SPECIFIERS[character]) {
treatment = character; // store the character to know how the attributedata should be treaded
i++; // character should be a "=" by now
}
else {
treatment = null;
}
attributeValue = '';
stringMarker = selectorItem[i+1];
attributeisString = (stringMarker==='"') || (stringMarker==="'");
attributeisString && (i++);
// end of attributaValue = (character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker))
while ((++i<len) && (character=selectorItem[i]) && !((character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker)))) {
attributeValue += character;
}
if (attributeisString) {
// if attribute is string, then we need to _remove to last stringmarker
attributeValue = attributeValue.substr(0, attributeValue.length-1);
}
else {
// if attribute is no string, then we need to typecast its value
isBoolean = ((attributeValue.length>3) && (attributeValue.length<6) &&
(checkBoolean=attributeValue.toUpperCase()) &&
((checkBoolean==='FALSE') || (checkBoolean==='TRUE')));
// typecast the value to either Boolean or Number:
attributeValue = isBoolean ? (checkBoolean==='TRUE') : parseFloat(attributeValue);
}
// depending upon how the attributedata should be treated:
if (treatment) {
switch (treatment) {
case '^': // “begins with” selector
if (!vnode.attrs[attributeName].startsWith(attributeValue)) {
return false;
}
break;
case '$': // “ends with” selector
if (!vnode.attrs[attributeName].endsWith(attributeValue)) {
return false;
}
break;
case '*': // “contains” selector (might be a substring)
if (!vnode.attrs[attributeName].contains(attributeValue)) {
return false;
}
break;
case '~': // “contains” selector as a separate word, separated by spaces
if (!(' '+vnode.attrs[attributeName]+' ').contains(' '+attributeValue+' ')) {
return false;
}
break;
case '|': // “contains” selector as a separate word, separated by `|`
if (!('|'+vnode.attrs[attributeName]+'|').contains('|'+attributeValue+'|')) {
return false;
}
break;
}
}
else if (vnode.attrs[attributeName]!==attributeValue) {
return false;
}
// we still need to increase one position:
(++i<len) && (character=selectorItem[i]);
break;
case ':':
// we have a pseudo-selector
// first, find out which one
// because '::' is a valid start (though without any selection), we start to back the next character as well:
pseudo = ':'+selectorItem[++i];
pseudoArg = '';
vParent = vnode.vParent;
vParentVChildren = vParent && vParent.vChildren;
// pseudo-selectors might have an argument passed in, like `:nth-child(2n+1)` or `:not([type="checkbox"])` --> we
// store this argument inside `pseudoArg`
// also note that combinations are possible with `:not` --> `:not(:nth-child(2n+1))`
// also note that we cannot "just" look for a closing character when running into the usage of attributes:
// for example --> `:not([data-x="some data :)"])`
// that's why -once we are inside attribute-data- we need to continue until the attribute-data ends
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
if (character==='(') {
// starting arguments
arglevel = 1;
insideAttribute = false;
insideAttributeValue = false;
while ((++i<len) && (character=selectorItem[i]) && (arglevel>0)) {
if (!insideAttribute) {
if (character==='(') {
arglevel++;
}
else if (character===')') {
arglevel--;
}
else if (character==='[') {
insideAttribute = true;
}
}
else {
// inside attribute
if (!insideAttributeValue) {
if ((character==='"') || (character==="'")) {
insideAttributeValue = true;
stringMarker = character;
}
else if (character===']') {
insideAttribute = false;
}
}
else if ((character===stringMarker) && (selectorItem[i+1]===']')) {
insideAttributeValue = false;
}
}
(arglevel>0) && (pseudoArg+=character);
}
}
else {
pseudo += character;
}
}
// now, `pseudo` is known as well as its possible pseudoArg
if (!vParentVChildren && PSEUDO_REQUIRED_CHILDREN[pseudo]) {
return false;
}
switch (pseudo) {
case ':checked': // input:checked Selects every checked <input> element
if (!vnode.attrs.checked) {
return false;
}
break;
case ':disabled': // input:disabled Selects every disabled <input> element
if (!vnode.attrs.disabled) {
return false;
}
break;
case ':empty': // p:empty Selects every <p> element that has no children (including text nodes)
if (vnode.vChildNodes && (vnode.vChildNodes.length>0)) {
return false;
}
break;
case ':enabled': // input:enabled Selects every enabled <input> element
if (vnode.attrs.disabled) {
return false;
}
break;
case PSEUDO_FIRST_CHILD: // p:first-child Selects every <p> element that is the first child of its parent
if (vParentVChildren[0]!==vnode) {
return false;
}
break;
case PSEUDO_FIRST_OF_TYPE: // p:first-of-type Selects every <p> element that is the first <p> element of its parent
for (k=vParentVChildren.indexOf(vnode)-1; k>=0; k--) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':focus': // input:focus Selects the input element which has focus
if (vnode.domNode!==DOCUMENT.activeElement) {
return false;
}
break;
case ':in-range': // input:in-range Selects input elements with a value within a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || (value<min) || (value>max)) {
return false;
}
break;
case ':lang': // p:lang(it) Selects every <p> element with a lang attribute equal to "it" (Italian)
if (vnode.attrs.lang!==pseudoArg) {
return false;
}
break;
case PSEUDO_LAST_CHILD: // p:last-child Selects every <p> element that is the last child of its parent
if (vParentVChildren[vParentVChildren.length-1]!==vnode) {
return false;
}
break;
case PSEUDO_LAST_OF_TYPE: // p:last-of-type Selects every <p> element that is the last <p> element of its parent
len2 = vParentVChildren.length;
for (k=vParentVChildren.indexOf(vnode)+1; k<len2; k++) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':not': // :not(p) Selects every element that is not a <p> element
if (vnode.matchesSelector(pseudoArg)) {
return false;
}
break;
case PSEUDO_NTH_CHILD: // p:nth-child(2) Selects every <p> element that is the second child of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
index = vParentVChildren.indexOf(vnode)+1;
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_CHILD: // p:nth-last-child(2) Selects every <p> element that is the second child of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_OF_TYPE: // p:nth-last-of-type(2) Selects every <p> element that is the second <p> element of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
index = 0;
for (k=vParentVChildren.length-1; (k>=0) && !found; k--) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_OF_TYPE: // p:nth-of-type(2) Selects every <p> element that is the second <p> element of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
len2 = vParentVChildren.length;
index = 0;
for (k=0; (k<len2) && !found; k++) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_ONLY_OF_TYPE: // p:only-of-type Selects every <p> element that is the only <p> element of its parent
len2 = vParentVChildren.length;
count = 0;
for (k=0; (k<len2) && (count<=1); k++) {
(vParentVChildren[k].tag===vnode.tag) && count++;
}
if (count!==1) {
return false;
}
break;
case PSEUDO_ONLY_CHILD: // p:only-child Selects every <p> element that is the only child of its parent
if (vParentVChildren.length!==1) {
return false;
}
break;
case ':optional': // input:optional Selects input elements with no "required" attribute
if (vnode.attrs.required) {
return false;
}
break;
case ':out-of-range': // input:out-of-range Selects input elements with a value outside a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || ((value>=min) && (value<=max))) {
return false;
}
break;
case ':read-only': // input:read-only Selects input elements with the "readonly" attribute specified
if (!vnode.attrs.readonly) {
return false;
}
break;
case ':read-write': // input:read-write Selects input elements with the "readonly" attribute NOT specified
if (vnode.attrs.readonly) {
return false;
}
break;
case ':required': // input:required Selects input elements with the "required" attribute specified
if (!vnode.attrs.required) {
return false;
}
break;
case ':root': // Selects the document's root element
if (vnode.domNode!==DOCUMENT.documentElement) {
return false;
}
break;
}
}
}
return true;
};
/**
* Splits the selector into separate subselector-items that should match different elements through the tree.
* Special characters '>' and '+' are added as separate items in the hash.
*
* @method _splitSelector
* @param selector {String} the selector-item to check the match for
* @return {Array} splitted selectors
* @protected
* @private
* @since 0.0.1
*/
_splitSelector = function(selector) {
var list = [],
len = selector.length,
sel = '',
i, character, insideDataAttr;
for (i=0; i<len; i++) {
character = selector[i];
if (character==='[') {
sel += character;
insideDataAttr = true;
}
else if (character===']') {
sel += character;
insideDataAttr = false;
}
else if (insideDataAttr || !SPLIT_CHARACTER[character]) {
sel += character;
}
else {
// unique selectoritem is found, add it to the list
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
// in case the last character was '>', '+' or '~', we need to add it as a separate item
STORABLE_SPLIT_CHARACTER[character] && (list[list.length]=character);
}
}
// add the last item
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
return list;
};
_batchEmit = function() {
// we will exactly define the 'UI:'-event --> Itags will have another emitterName
MUTATION_EVENTS.each(function (mutationEvents, vnode) {
var domNode = vnode.domNode;
if (mutationEvents[EV_REMOVED]) {
domNode.emit('UI:'+EV_REMOVED);
}
else if (mutationEvents[EV_INSERTED]) {
domNode.emit('UI:'+EV_INSERTED);
}
else {
// contentchange and attributechanges can go hand in hand
mutationEvents.each(function(value, evt) {
domNode.emit('UI:'+evt, (evt===EV_CONTENT_CHANGE) ? null : {changed: value});
});
}
});
MUTATION_EVENTS.clear();
BATCH_WILL_RUN = false;
};
_emitDestroyChildren = function(vnode) {
var children = vnode.vChildren,
len = children.length,
i, vChild;
for (i=0; i<len; i++) {
vChild = children[i];
vChild._emit(EV_REMOVED);
}
};
_markRemoved = function(vnode) {
var vChildNodes = vnode.vChildNodes,
len, i, vChildNode;
if (vnode.nodeType===1) {
Object.protectedProp(vnode, 'removedFromDOM', true);
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && _markRemoved(vChildNode);
}
}
}
};
/**
* A safe way to remove a dom-node, even if it is not present.
* Will not throw a JS error when the "to be removed" node isn't in the dom
*
* @method _tryRemoveDomNode
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param childDomNode {DOMNode} the node that needs to be removed
* @since 0.0.1
*/
_tryRemoveDomNode = function(parentDomNode, childDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && childDomNode.isItag && childDomNode.isItag()) {
DOCUMENT._itagList.remove(childDomNode);
}
try {
parentDomNode._removeChild(childDomNode);
}
catch(err) {}
};
/**
* A safe way to replace a dom-node, even if the "to be replaced"-node it is not present.
* Will not throw a JS error when the "to be replaced" node isn't in the dom. in that case, the new node will
* be appended
*
* @method _tryReplaceChild
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param newChildDomNode {DOMNode} the node that needs to be replaced
* @param oldChildDomNode {DOMNode} the node that needs to be inserted
* @since 0.0.1
*/
_tryReplaceChild = function(parentDomNode, newChildDomNode, oldChildDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && oldChildDomNode.isItag && oldChildDomNode.isItag()) {
DOCUMENT._itagList.remove(oldChildDomNode);
}
try {
parentDomNode._replaceChild(newChildDomNode, oldChildDomNode);
}
catch(err) {
// if the childDomNode isn;t there any more - for whatever reason - the we need to append the newChildNode
parentDomNode._appendChild(newChildDomNode);
}
};
vNodeProto = window._ITSAmodules.VNode = {
/**
* Check whether the vnode's domNode is equal, or contains the specified Element.
*
* @method contains
* @return {Boolean} whether the vnode's domNode is equal, or contains the specified Element.
* @since 0.0.1
*/
contains: function(otherVNode, noProtectedSearch) {
var instance = this;
if (otherVNode && (otherVNode.destroyed || (noProtectedSearch && otherVNode._systemNode))) {
return false;
}
while (otherVNode && (otherVNode!==instance)) {
otherVNode = otherVNode.vParent;
if (otherVNode && noProtectedSearch && (otherVNode._systemNode || (otherVNode.isItag && otherVNode.domNode.contentHidden))) {
return false;
}
}
return (otherVNode===instance);
},
/**
* Empties the vnode.
*
* Syncs with the dom.
*
* @method empty
* @param [full=false] {Boolean} whether system-nodes should be removed as well
* @chainable
* @since 0.0.1
*/
empty: function(full) {
delete this._scripts;
return this._setChildNodes([], null, full);
},
/**
* Returns the first child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method firstOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the first child-vnode or null when not present
* @since 0.0.1
*/
firstOfVChildren: function(cssSelector) {
var instance = this,
found, i, len, vChildren, element;
if (!cssSelector) {
return instance.vFirstElementChild;
}
vChildren = instance.vChildren;
len = vChildren.length;
for (i=0; !found && (i<len); i++) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
return found;
},
/**
* Gets the innerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* Only valid for nodetype=1 (HTMLElements)
*
* @method getHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String|undefined} the innerHTML without the elements specified, or `undefined` when not an HTMLElement
* @since 0.0.1
*/
getHTML: function(exclude, includeSystemNodes) {
var instance = this,
html, vChildNodes, len, i, vChildNode;
if (instance.nodeType===1) {
if (exclude) {
Array.isArray(exclude) || (exclude=[exclude]);
}
html = '';
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
switch (vChildNode.nodeType) {
case 1:
(exclude && exclude.contains(vChildNode.domNode)) || (!includeSystemNodes && vChildNode._systemNode) || (html+=vChildNode.getOuterHTML(exclude, includeSystemNodes));
break;
case 3:
html += vChildNode.text.replace(/</g, '<').replace(/>/g, '>');
break;
case 8:
html += '<!--' + vChildNode.text.replace(/</g, '<').replace(/>/g, '>') + '-->';
}
}
}
return html;
},
/**
* Gets the outerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* @method getOuterHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String} the outerHTML
* @since 0.0.1
*/
getOuterHTML: function(exclude, includeSystemNodes) {
var instance = this,
html,
attrs = instance.attrs;
if (instance.nodeType===1) {
if (instance.nodeType!==1) {
return instance.textContent;
}
html = '<' + instance.tag.toLowerCase();
attrs.each(function(value, key) {
html += ' '+key+'="'+value+'"';
});
instance.isVoid && (html += '/');
html += '>';
if (!instance.isVoid) {
html += ((!includeSystemNodes && instance.isItag) ? '' : instance.getHTML(exclude, includeSystemNodes)) + '</' + instance.tag.toLowerCase() + '>';
}
}
return html;
},
/**
* Checks whether the vnode has any vChildNodes (nodeType of 1, 3 or 8).
*
* @method hasVChildNodes
* @return {Boolean} whether the vnode has any vChildNodes.
* @since 0.0.1
*/
hasVChildNodes: function() {
return this.vChildNodes ? (this.vChildNodes.length>0) : false;
},
/**
* Checks whether the vnode has any vChildren (vChildNodes with nodeType of 1).
*
* @method hasVChildren
* @return {Boolean} whether the vnode has any vChildren.
* @since 0.0.1
*/
hasVChildren: function() {
return this.vChildNodes ? (this.vChildren.length>0) : false;
},
/**
* Checks whether the className is present on the vnode.
*
* @method hasClass
* @param className {String|Array} the className to check for. May be an Array of classNames, which all needs to be present.
* @return {Boolean} whether the className (or classNames) is present on the vnode
* @since 0.0.1
*/
hasClass: function(className) {
var instance = this,
check = function(cl) {
return !!instance.classNames[cl];
};
if (!instance.classNames) {
return false;
}
if (typeof className === STRING) {
return check(className);
}
else if (Array.isArray(className)) {
return className.every(check);
}
return false;
},
/**
* Returns the last child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method lastOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the last child-vnode or null when not present
* @since 0.0.1
*/
lastOfVChildren: function(cssSelector) {
var vChildren = this.vChildren,
found, i, element;
if (vChildren) {
if (!cssSelector) {
return this.vLastElementChild;
}
for (i=vChildren.length-1; !found && (i>=0); i--) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
}
return found;
},
/**
* Checks whether the vnode matches one of the specified selectors. `selectors` can be one, or multiple css-selectors,
* separated by a `comma`. For example: "#myid li.red blue" is one selector, "div.red, div.blue, div.green" are three selectors.
*
* @method matchesSelector
* @param selectors {String} one or more css-selectors
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches one of the selectors
* @since 0.0.1
*/
matchesSelector: function(selectors, relatedVNode) {
var instance = this;
if (instance.nodeType!==1) {
return false;
}
selectors = selectors.split(',');
// we can use Array.some, because there won't be many separated selectoritems,
// so the final invocation won't be delayed much compared to looping
return selectors.some(function(selector) {
return _matchesOneSelector(instance, selector, relatedVNode);
});
},
/**
* Reloads the DOM-attribute into the vnode.
*
* @method matchesSelector
* @param attributeName {String} the name of the attribute to be reloaded.
* @return {Node} the domNode that was reloaded.
* @since 0.0.1
*/
reloadAttr: function(attributeName) {
var instance = this,
domNode = instance.domNode,
attributeValue = domNode._getAttribute(attributeName),
attrs = instance.attrs,
extractStyle, extractClass;
if (instance.nodeType===1) {
attributeValue || (attributeValue='');
if (attributeValue==='') {
delete attrs[attributeName];
// in case of STYLE attributeName --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attributeName --> special treatment
(attributeName===CLASS) && (instance.classNames={});
// in case of ID attributeName --> special treatment
if ((attributeName===ID) && (instance.id)) {
delete nodeids[instance.id];
delete instance.id;
}
}
else {
attrs[attributeName] = attributeValue;
// in case of STYLE attributeName --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(attributeValue);
attributeValue = extractStyle.attrStyle;
if (attributeValue) {
attrs.style = attributeValue;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attributeName --> special treatment
extractClass = extractor.extractClass(attributeValue);
attributeValue = extractClass.attrClass;
if (attributeValue) {
attrs[CLASS] = attributeValue;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (instance.id!==attributeValue) && (delete nodeids[instance.id]);
instance.id = attributeValue;
nodeids[attributeValue] = domNode;
}
}
}
return domNode;
},
/**
* Returns the vnode's style in a serialized form: the way it appears in the dom.
*
* @method serializeStyles
* @return {String} vnode's style
* @since 0.0.1
*/
serializeStyles: function() {
return extractor.serializeStyles(this.styles);
},
/**
* Sets the vnode's and dom-nodes inner HTML.
*
* Syncs with the dom. Can be invoked multiple times without issues.
*
* @method setHTML
* @param content {String} the innerHTML
* @param [suppressItagRender] {Boolean} to suppress Itags from rendering
* @param [allowScripts=false] {Boolean} whether scripts are allowed --> these should be defined with `xscript` instead of `script`
* @chainable
* @since 0.0.1
*/
setHTML: function(content, suppressItagRender, allowScripts) {
var instance = this;
instance._setChildNodes(htmlToVNodes(content, vNodeProto, instance.ns, null, suppressItagRender, allowScripts), suppressItagRender);
return instance;
},
/**
* Syncs the vnode's nodeid (if available) inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom. Can be invoked multiple times without issues.
*
* @method storeId
* @chainable
* @since 0.0.1
*/
storeId: function() {
// store node/vnode inside WeakMap:
var instance = this;
instance.id ? (nodeids[instance.id]=instance.domNode) : (delete nodeids[instance.id]);
return instance;
},
//---- private ------------------------------------------------------------------
_addToTaglist: function() {
var instance = this,
itagList;
if (instance.isItag) {
itagList = DOCUMENT.getItags(); // also reads the dom if the list isn't build yet
instance._data || Object.protectedProp(instance, '_data', {});
if (!instance._data.ce_destroyed && !itagList.contains(instance.domNode)) {
itagList.push(instance.domNode);
}
}
},
/**
* Adds a vnode to the end of the list of vChildNodes.
*
* Syncs with the DOM.
*
* @method _appendChild
* @param VNode {vnode} vnode to append
* @private
* @return {Node} the Node that was appended
* @since 0.0.1
*/
_appendChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
size, noProccessScript, scriptContent;
if ((VNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (VNode.attrs && VNode.attrs.src) ? VNode.attrs.src : VNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
VNode._moveToParent(instance);
instance.domNode._appendChild(domNode);
if (VNode.nodeType===3) {
size = instance.vChildNodes.length;
instance._normalize();
// if the size changed, then the domNode was merged
(size===instance.vChildNodes.length) || (domNode=instance.vChildNodes[instance.vChildNodes.length-1].domNode);
}
if (VNode.nodeType===1) {
VNode._addToTaglist();
VNode._emit(EV_INSERTED);
}
return domNode;
}
},
/**
* Removes the vnode from its parent vChildNodes- and vChildren-list.
*
* Does NOT sync with the dom.
*
* @method _deleteFromParent
* @private
* @chainable
* @since 0.0.1
*/
_deleteFromParent: function() {
var instance = this,
vParent = instance.vParent;
if (vParent && vParent.vChildNodes) {
vParent.vChildNodes.remove(instance);
// force to recalculate the vChildren on a next call:
(instance.nodeType===1) && (vParent._vChildren=null);
}
return instance;
},
/**
* Cleans up (empties) vnode's `_data`
*
* @method _cleanData
* @private
* @chainable
* @since 0.0.1
*/
_cleanData: function() {
var instance = this,
data = instance._data;
data && data.each(
function(value, key) {
delete data[key];
}
);
return instance;
},
/**
* Cleans up (removes) duplicated `style` definitions.
*
* @method _cleanupStyle
* @private
* @chainable
* @since 0.0.1
*/
_cleanupStyle: function() {
var instance = this,
compare = [],
removal = [],
vChildren = instance.vChildren,
len = vChildren.length,
vChild, i, styleContent, vChildInner;
for (i=0; i<len; i++) {
vChild = vChildren[i];
if (vChild.tag==='STYLE') {
vChildInner = vChild.vChildNodes[0];
if (vChildInner) {
styleContent = vChildInner.text;
if (compare.contains(styleContent)) {
removal[removal.length] = vChild;
}
else {
compare[compare.length] = styleContent;
}
}
else {
// empty style-tag
removal[removal.length] = vChild;
}
}
}
removal.forEach(function(vnode) {
instance._removeChild(vnode);
});
},
/**
* Destroys the vnode and all its vnode-vChildNodes.
* Removes it from its vParent.vChildNodes list,
* also removes its definitions inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom.
*
* @method _destroy
* @private
* @chainable
* @since 0.0.1
*/
_destroy: function(silent) {
var instance = this,
vChildNodes = instance.vChildNodes,
len, i, vChildNode, vParent, treeNodes;
if (!instance.destroyed) {
if (!silent) {
// Because we don't want to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
later(function() {
instance._emit(EV_REMOVED, null, null, null, true);
}, 5);
}
Object.protectedProp(instance, 'destroyed', true);
// first: determine the dom-tree, which module `event-dom` needs to determine where the node was before it was destroyed:
treeNodes = [instance];
vParent = instance.vParent;
while (vParent) {
treeNodes[treeNodes.length] = vParent;
vParent = vParent.vParent;
}
// mark all its vChildNodes so we can see if the node is in the DOM
_markRemoved(instance);
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && instance.isItag) {
DOCUMENT._itagList.remove(instance.domNode);
}
instance._scripts && (instance._scripts.length=0);
// The definite cleanup needs to be done after a timeout:
// someone might need to handle the Element when removed (fe to cleanup specific things)
later(function() {
instance._cleanData();
if (instance.nodeType===1) {
// _destroy all its vChildNodes
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && vChildNode._destroy(true);
}
}
}
instance._vChildren = null;
// explicitely set instance.domNode._vnode and instance.domNode to null in order to prevent problems with the GC (we break the circular reference)
delete instance.domNode._vnode;
// if valid id, then _remove the DOMnodeRef from internal hash
instance.id && delete nodeids[instance.id];
}, silent ? 0 : DESTROY_DELAY);
instance._deleteFromParent();
// Do not make domNode `null` --> it could be used even when not in the dom
}
return instance;
},
_emit: function(evt, attribute, newValue, prevValue, destroyEvt) {
/**
* Emitted by every Element that gets inserted.
*
* @event nodeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets removed.
*
* @event noderemove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets its content changed (innerHTML/innerText).
*
* @event nodecontentchange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute inserted.
*
* @event attributeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* </ul>
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute removed.
*
* @event attributeremove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Strings of the attributeNames that are removed
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute changed.
*
* @event attributechange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* <li>prevValue</li>
* </ul>
* @since 0.1
*/
var instance = this,
silent, attrMutations, mutationEvents, mutation, vParent;
if (!DOCUMENT.hasMutationSubs || (instance.nodeType!==1)) {
return;
}
silent = !!DOCUMENT._suppressMutationEvents;
if (!silent && (destroyEvt || !instance.destroyed)) {
// Because we don't wannt to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
mutationEvents = MUTATION_EVENTS.get(instance) || {};
if (attribute) {
attrMutations = mutationEvents[evt] || [];
if (evt===EV_ATTRIBUTE_REMOVED) {
mutation = attribute;
}
else {
mutation = {
attribute: attribute
};
if ((evt===EV_ATTRIBUTE_INSERTED) || (evt===EV_ATTRIBUTE_CHANGED)) {
mutation.newValue = newValue;
}
if ((evt===EV_ATTRIBUTE_CHANGED) && prevValue) {
mutation.prevValue = prevValue;
}
}
attrMutations.push(mutation);
mutationEvents[evt] = attrMutations;
}
else {
mutationEvents[evt] = true;
}
MUTATION_EVENTS.set(instance, mutationEvents);
// now set all parent to have a nodecontentchange:
vParent = instance.vParent;
vParent && vParent._emit(EV_CONTENT_CHANGE);
// in case of removal we need to emit EV_REMOVED for all children right now
// for they will be actually removed silently after a delay of 1 minute
(evt===EV_REMOVED) && _emitDestroyChildren(instance);
if (!BATCH_WILL_RUN) {
BATCH_WILL_RUN = true;
async(function() {
_batchEmit();
});
}
}
return instance;
},
/**
* Inserts `newVNode` before `refVNode`.
*
* Syncs with the DOM.
*
* @method _insertBefore
* @param newVNode {vnode} vnode to insert
* @param refVNode {vnode} The vnode before which newVNode should be inserted.
* @private
* @return {Node} the Node being inserted (equals domNode)
* @since 0.0.1
*/
_insertBefore: function(newVNode, refVNode) {
var instance = this,
domNode = newVNode.domNode,
index = instance.vChildNodes.indexOf(refVNode),
noProccessScript, scriptContent;
if ((newVNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (newVNode.attrs && newVNode.attrs.src) ? newVNode.attrs.src : newVNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
if (index!==-1) {
newVNode._moveToParent(instance, index);
instance.domNode._insertBefore(domNode, refVNode.domNode);
(newVNode.nodeType===3) && instance._normalize();
if (newVNode.nodeType===1) {
newVNode._addToTaglist();
newVNode._emit(EV_INSERTED);
}
return domNode;
}
else {
console.warn('trying to insert before, but no ref-node found. Will append the new node');
return instance._appendChild(newVNode);
}
}
},
/**
* Moves the vnode from its current parent.vChildNodes list towards a new parent vnode at the specified position.
*
* Does NOT sync with the dom.
*
* @method _moveToParent
* @param parentVNode {vnode} the parent-vnode
* @param [index] {Number} the position of the child. When not specified, it will be appended.
* @private
* @chainable
* @since 0.0.1
*/
_moveToParent: function(parentVNode, index) {
var instance = this,
vParent = instance.vParent;
instance._deleteFromParent();
instance.vParent = parentVNode;
parentVNode.vChildNodes || (parentVNode.vChildNodes=[]);
(typeof index==='number') ? parentVNode.vChildNodes.insertAt(instance, index) : (parentVNode.vChildNodes[parentVNode.vChildNodes.length]=instance);
// force to recalculate the vChildren on a next call:
vParent && (instance.nodeType===1) && (vParent._vChildren = null);
// force to recalculate the vChildren on a next call:
parentVNode && (instance.nodeType===1) && (parentVNode._vChildren=null);
return instance;
},
/**
* Removes empty TextNodes and merges following TextNodes inside the vnode.
*
* Syncs with the dom.
*
* @method _normalize
* @private
* @chainable
* @since 0.0.1
*/
_normalize: function() {
var instance = this,
domNode = instance.domNode,
vChildNodes = instance.vChildNodes,
changed = false,
i, preChildNode, vChildNode;
if (!instance._unNormalizable && vChildNodes) {
for (i=vChildNodes.length-1; i>=0; i--) {
vChildNode = vChildNodes[i];
preChildNode = vChildNodes[i-1]; // i will get the value `-1` eventually, which leads into undefined preChildNode
if (vChildNode.nodeType===3) {
if (vChildNode.text==='') {
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
else if (preChildNode && preChildNode.nodeType===3) {
preChildNode.text += vChildNode.text;
preChildNode.domNode.nodeValue = unescapeEntities(preChildNode.text);
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
}
}
}
changed && instance._emit(EV_CONTENT_CHANGE);
return instance;
},
/**
* Makes the vnode `normalizable`. Could be set to `false` when batch-inserting nodes, while `normalizaing` manually at the end.
* Afterwards, you should always reset `normalizable` to true.
*
* @method _normalizable
* @param value {Boolean} whether the vnode should be normalisable.
* @private
* @chainable
* @since 0.0.1
*/
_normalizable: function(value) {
var instance = this;
value ? (delete instance._unNormalizable) : (instance._unNormalizable=true);
return instance;
},
/**
* Prevents MutationObserver from making the dom sync with the vnode.
* Should be used when manipulating the dom from within the vnode itself (to preventing looping)
*
* @method _noSync
* @chainable
* @private
* @since 0.0.1
*/
_noSync: function() {
var instance = this;
if (!instance._nosync) {
instance._nosync = true;
async(function() {
instance._nosync = false;
});
}
return instance;
},
/**
* Removes the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _removeAttr
* @param attributeName {String}
* @private
* @chainable
* @since 0.0.1
*/
_removeAttr: function(attributeName, suppressItagRender) {
var instance = this,
attributeNameSplitted, ns;
if (instance.isItag && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
console.warn('Not allowed to remove the attribute '+attributeName);
return instance;
}
if (instance.attrs[attributeName]!==undefined) {
delete instance.attrs[attributeName];
// in case of STYLE attribute --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attribute --> special treatment
(attributeName===CLASS) && (instance.classNames={});
if (attributeName===ID) {
delete nodeids[instance.id];
delete instance.id;
}
instance._emit(EV_ATTRIBUTE_REMOVED, attributeName);
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
instance.domNode._removeAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName);
}
else {
instance.domNode._removeAttribute(attributeName);
}
}
return instance;
},
/**
* Removes the vnode's child-vnode from its vChildren and the DOM.
*
* Syncs with the DOM.
*
* @method removeChild
* @param VNode {vnode} the child-vnode to remove
* @private
* @since 0.0.1
*/
_removeChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
hadFocus = domNode && domNode.hasFocus && domNode.hasFocus() && (VNode.attrs['fm-lastitem']==='true'),
parentVNode = VNode.vParent;
VNode._destroy();
_tryRemoveDomNode(instance.domNode, VNode.domNode);
instance._normalize();
// now, reset the focus on focusmanager when needed:
if (hadFocus) {
while (parentVNode && !parentVNode.attrs['fm-manage']) {
parentVNode = parentVNode.vParent;
}
parentVNode && parentVNode.domNode.focus();
}
},
/**
* Replaces the current vnode at the parent.vChildNode list by `newVNode`
*
* Does NOT sync with the dom.
*
* @method _replaceAtParent
* @param newVNode {Object} the new vnode which should take over the place of the current vnode
* @private
* @chainable
* @since 0.0.1
*/
_replaceAtParent: function(newVNode) {
var instance = this,
vParent = instance.vParent,
vChildNodes, index;
if (vParent && (vChildNodes=vParent.vChildNodes)) {
index = vChildNodes.indexOf(instance);
// force to recalculate the vChildren on a next call:
((instance.nodeType===1) || (newVNode.nodeType===1)) && (instance.vParent._vChildren=null);
vChildNodes[index] = newVNode;
}
return instance._destroy();
},
/**
* Sets the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _setAttr
* @param attributeName {String}
* @param value {String} the value for the attributeName
* @param [force=false] {Boolean} force the attribute to be set, even if restrictions would deny it
* @private
* @chainable
* @since 0.0.1
*/
_setAttr: function(attributeName, value, force, suppressItagRender) {
var instance = this,
extractStyle, extractClass,
attrs = instance.attrs,
prevVal = attrs[attributeName],
domNode = instance.domNode,
attributeNameSplitted, ns;
if (instance.isItag && !force && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
if (prevVal!=value) {
console.warn('Not allowed to set the attribute '+attributeName);
}
return instance;
}
// don't check by !== --> value isn't parsed into a String yet
if (prevVal && ((value===undefined) || (value===null))) {
instance._removeAttr(attributeName, suppressItagRender);
return instance;
}
// attribute-values are always Strings:
value = String(value);
// attribute-values will be stored without " or '
value = value.replace(/"/g, '"').replace(/'/g, "'");
if (prevVal!=value) {
attrs[attributeName] = value;
// in case of STYLE attribute --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(value);
value = extractStyle.attrStyle;
if (value) {
attrs.style = value;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attribute --> special treatment
extractClass = extractor.extractClass(value);
value = extractClass.attrClass;
if (value) {
attrs[CLASS] = value;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (delete nodeids[instance.id]);
instance.id = value;
nodeids[value] = domNode;
}
instance._emit(prevVal ? EV_ATTRIBUTE_CHANGED : EV_ATTRIBUTE_INSERTED, attributeName, value, prevVal);
// when set in the dom --> quotes need to be set as "
value = value.replace(/"/g, '"');
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
domNode._setAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName, value);
}
else {
domNode._setAttribute(attributeName, value);
}
}
return instance;
},
/**
* Redefines the attributes of both the vnode as well as its related dom-node. The new
* definition replaces any previous attributes (without touching unmodified attributes).
* the `is` attribute cannot be changed for itags.
*
* Syncs the new vnode's attributes with the dom.
*
* @method _setAttrs
* @param newAttrs {Object|Array} the new attributes to be set
* @private
* @chainable
* @since 0.0.1
*/
_setAttrs: function(newAttrs, suppressItagRender) {
// does sync the DOM
var instance = this,
attrsObj, attr, attrs, i, key, keys, len, value;
if (instance.nodeType!==1) {
return;
}
instance._noSync();
attrs = instance.attrs;
attrs.id && (delete nodeids[attrs.id]);
if (Object.isObject(newAttrs)) {
attrsObj = newAttrs;
}
else {
attrsObj = {};
len = newAttrs.length;
for (i=0; i<len; i++) {
attr = newAttrs[i];
attrsObj[attr.name] = attr.value;
}
}
if (instance.isItag && !suppressItagRender) {
if (attrs.is) {
attrsObj.is = attrs.is;
}
else {
delete attrsObj.is;
delete attrsObj.Is;
delete attrsObj.iS;
delete attrsObj.IS;
}
}
// first _remove the attributes that are no longer needed.
// quickest way for object iteration: http://jsperf.com/object-keys-iteration/20
keys = Object.keys(attrs);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
attrsObj[key] || instance._removeAttr(key, suppressItagRender);
}
// next: every attribute that differs: redefine
keys = Object.keys(attrsObj);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
value = attrsObj[key];
(attrs[key]===value) || instance._setAttr(key, value, suppressItagRender);
}
return instance;
},
/**
* Redefines the childNodes of both the vnode as well as its related dom-node. The new
* definition replaces any previous nodes. (without touching unmodified nodes).
*
* Syncs the new vnode's childNodes with the dom.
*
* @method _setChildNodes
* @param newVChildNodes {Array} array with vnodes which represent the new childNodes
* @private
* @chainable
* @since 0.0.1
*/
_setChildNodes: function(newVChildNodes, suppressItagRender, removeSystemElements) {
// does sync the DOM
var instance = this,
vChildNodes = instance.vChildNodes || [],
domNode = instance.domNode,
forRemoval = [],
i, oldChild, newChild, newLength, len, len2, childDomNode, nodeswitch, bkpAttrs, cleanupStyle,
process, bkpChildNodes, needNormalize, prevSuppress, scriptContent, _scripts, scriptLen, j;
instance._noSync();
// first: reset ._vChildren --> by making it empty, its getter will refresh its list on a next call
instance._vChildren = null;
// if newVChildNodes is undefined, then we assume it to be empty --> an empty array
newVChildNodes || (newVChildNodes=[]);
// quickest way to loop through array is by using for loops: http://jsperf.com/array-foreach-vs-for-loop/5
len = vChildNodes.length;
// we need to add the systemNodes -if any- to the new newVChildNodes: they should be retained.
// SystemNodes are always places at the beginning, so they won't get reshuffled:
if (!removeSystemElements) {
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
if (oldChild._systemNode) {
newVChildNodes.insertAt(oldChild, i);
}
else {
break;
}
}
}
newLength = newVChildNodes.length;
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
childDomNode = oldChild.domNode;
if (i < newLength) {
newChild = newVChildNodes[i];
newChild.vParent || (newChild.vParent=instance);
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
/*jshint boss:true */
switch (nodeswitch=NODESWITCH[oldChild.nodeType][newChild.nodeType]) {
/*jshint boss:false */
case 1: // oldNodeType==Element, newNodeType==Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
if ((oldChild.tag!==newChild.tag) ||
((oldChild.tag===newChild.tag) && oldChild.isItag && (oldChild.attrs.is!==newChild.attrs.is))) {
// new tag --> completely replace
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
/*jshint proto:true */
oldChild.isItag && oldChild.domNode.destroyUI && oldChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
newChild.attrs = {}; // reset to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
oldChild._replaceAtParent(newChild);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
else {
// same tag --> only update what is needed
// NOTE: when this._unchangableAttrs exists, an itag-element syncs its UI -->
if (oldChild._data) {
// we might need to set the class `itag-rendered` when the attributeData says so:
// this happens when an itag gets refreshed with an unrendered definition
if (oldChild._data.itagRendered && !newChild.hasClass('itag-rendered')) {
newChild.classNames['itag-rendered'] = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' '+'itag-rendered';
}
else {
newChild.attrs[CLASS] = 'itag-rendered';
}
}
// we might need to set the class `focussed` when the attributeData says so:
// this happens when an itag gets rerendered: its renderFn doesn't know if any elements
// were focussed
if (oldChild._data.focussed && !newChild.hasClass('focussed')) {
newChild.classNames.focussed = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' ' + 'focussed';
}
else {
newChild.attrs[CLASS] = 'focussed';
}
}
if (oldChild._data['fm-tabindex']) {
// node has the tabindex set by the focusmanager,
// but that info might got lost with re-rendering of the new element
newChild.attrs.tabindex = '0';
}
}
if (oldChild.isItag) {
prevSuppress = DOCUMENT._suppressMutationEvents || false;
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
/*jshint proto:true */
newChild.domNode.destroyUI && newChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
oldChild._setAttrs(newChild.attrs, suppressItagRender);
newChild._destroy(true); // destroy through the vnode and removing from DOCUMENT._itagList
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(prevSuppress);
}
else {
oldChild._setAttrs(newChild.attrs, suppressItagRender);
// next: sync the vChildNodes:
oldChild._setChildNodes(newChild.vChildNodes, suppressItagRender);
}
// reset ref. to the domNode, for it might have been changed by newChild:
oldChild.id && (nodeids[oldChild.id]=childDomNode);
newVChildNodes[i] = oldChild;
}
}
break;
case 2: // oldNodeType==Element, newNodeType==TextNode
// case2 and case3 should be treated the same
case 3: // oldNodeType==Element, newNodeType==Comment
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
oldChild._replaceAtParent(newChild);
instance._emit(EV_CONTENT_CHANGE);
break;
case 4: // oldNodeType==TextNode, newNodeType==Element
// case4 and case7 should be treated the same
case 7: // oldNodeType==Comment, newNodeType==Element
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
// oldChild.isVoid = newChild.isVoid;
// delete oldChild.text;
instance._emit(EV_CONTENT_CHANGE);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
break;
case 5: // oldNodeType==TextNode, newNodeType==TextNode
// case5 and case9 should be treated the same
case 9: // oldNodeType==Comment, newNodeType==Comment
if (oldChild.text!==newChild.text) {
oldChild.text = newChild.text;
oldChild.domNode.nodeValue = unescapeEntities(newChild.text);
instance._emit(EV_CONTENT_CHANGE);
}
newVChildNodes[i] = oldChild;
break;
case 6: // oldNodeType==TextNode, newNodeType==Comment
// case6 and case8 should be treated the same
case 8: // oldNodeType==Comment, newNodeType==TextNode
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = oldChild.vParent;
instance._emit(EV_CONTENT_CHANGE);
}
if ((nodeswitch===2) || (nodeswitch===5) || (nodeswitch===8)) {
needNormalize = true;
}
}
else {
// _remove previous definition
_tryRemoveDomNode(domNode, oldChild.domNode);
// the oldChild needs to be removed, however, this cannot be done right now, for it would effect the loop
// so we store it inside a hash to remove it later
forRemoval[forRemoval.length] = oldChild;
}
}
// now definitely remove marked childNodes:
len2 = forRemoval.length;
for (i=0; i<len2; i++) {
forRemoval[i]._destroy();
}
// now we add all new vChildNodes that go beyond `len`:
for (i = len; i < newLength; i++) {
newChild = newVChildNodes[i];
newChild.vParent = instance;
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
switch (newChild.nodeType) {
case 1: // Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
domNode._appendChild(newChild.domNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
break;
case 3: // TextNode
needNormalize = true;
// we need to break through --> no `break`
/* falls through */
default: // TextNode or CommentNode
// newChild.domNode.nodeValue = newChild.text;
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
domNode._appendChild(newChild.domNode);
instance._emit(EV_CONTENT_CHANGE);
}
newChild.storeId();
}
instance.vChildNodes = newVChildNodes;
needNormalize && instance._normalize();
cleanupStyle && instance._cleanupStyle();
return instance;
},
_setUnchangableAttrs: function(unchangableObj) {
this._unchangableAttrs = unchangableObj;
}
};
//---- properties ------------------------------------------------------------------
/**
* A hash of all the `attributes` of the vnode's representing dom-node.
*
* @property attrs
* @type Object
* @since 0.0.1
*/
/**
* Hash with all the classes of the vnode. Every class represents a key, all values are set `true`.
*
* @property classNames
* @type Object
* @since 0.0.1
*/
/**
* The `id` of the vnode's representing dom-node (if any).
*
* @property id
* @type String
* @since 0.0.1
*/
/**
* Tells whether tag is a void Element. Examples are: `br`, `img` and `input`. Non-void Elements are f.e. `div` and `table`.
* For TextNodes and CommentNodes, this property is `undefined`.
*
* @property isVoid
* @type Boolean
* @since 0.0.1
*/
/**
* The `nodeType` of the vnode's representing dom-node (1===ElementNode, 3===TextNode, 8===CommentNode).
*
* @property nodeType
* @type Number
* @since 0.0.1
*/
/**
* The `tag` of the vnode's representing dom-node (allways uppercase).
*
* @property tag
* @type String
* @since 0.0.1
*/
/**
* The `content` of the vnode's representing dom-node, in case it is a TextNode or CommentNode.
* Equals dom-node.nodeValue.
*
* Is `undefined` for ElementNodes.
*
* @property text
* @type String
* @since 0.0.1
*/
/**
* Hash with all the childNodes (vnodes). vChildNodes are any kind of vnodes (nodeType===1, 3 or 8)
*
* @property vChildNodes
* @type Array
* @since 0.0.1
*/
/**
* The underlying `dom-node` that the vnode represents.
*
* @property domNode
* @type domNode
* @since 0.0.1
*/
/**
* vnode's parentNode (defined as a vnode itself).
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
Object.defineProperties(vNodeProto, {
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property innerHTML
* @type String
* @since 0.0.1
*/
innerHTML: {
get: function() {
return this.getHTML();
},
set: function(v) {
this.setHTML(v);
}
},
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property nodeValue
* @type String
* @since 0.0.1
*/
nodeValue: {
get: function() {
var instance = this;
return ((instance.nodeType===3) || (instance.nodeType===8)) ? instance.text : null;
},
set: function(v) {
var instance = this,
newTextContent, prevTextContent;
if ((instance.nodeType===3) || (instance.nodeType===8)) {
prevTextContent = instance.domNode.textContent;
instance.domNode.textContent = v;
// set .text AFTER the dom-node is updated --> the content might be escaped!
newTextContent = instance.text = instance.domNode.textContent;
(newTextContent!==prevTextContent) && instance._emit(EV_CONTENT_CHANGE);
}
}
},
/**
* Gets or sets the outerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property outerHTML
* @type String
* @since 0.0.1
*/
outerHTML: {
get: function() {
return this.getOuterHTML();
},
set: function(v) {
var instance = this,
vParent = instance.vParent,
id = instance.attrs.id,
vnode, vnodes, bkpAttrs, bkpChildNodes, i, len, vChildNodes, isLastChildNode, index, refDomNode;
if ((instance.nodeType!==1) || !vParent) {
return;
}
instance._noSync();
vChildNodes = vParent.vChildNodes;
index = vChildNodes.indexOf(instance);
isLastChildNode = (index===(vChildNodes.length-1));
isLastChildNode || (refDomNode=vChildNodes[index+1].domNode);
vnodes = htmlToVNodes(v, vNodeProto, vParent.ns, vParent);
len = vnodes.length;
if (len>0) {
// the first vnode will replace the current instance:
vnode = vnodes[0];
if (vnode.nodeType===1) {
if (vnode.tag!==instance.tag) {
// new tag --> completely replace
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
id && (delete nodeids[id]);
vnode.attrs = {}; // reset to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
// vnode.attrs = bkpAttrs;
// vnode.vChildNodes = bkpChildNodes;
vnode.id && (nodeids[vnode.id]=vnode.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
else {
instance._setAttrs(vnode.attrs);
instance._setChildNodes(vnode.vChildNodes);
}
}
else {
id && (delete nodeids[id]);
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
}
for (i=1; i<len; i++) {
vnode = vnodes[i];
switch (vnode.nodeType) {
case 1: // Element
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
vnode.attrs = {}; // reset, to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._insertBefore(vnode.domNode, refDomNode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
break;
default: // TextNode or CommentNode
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._appendChild(vnode.domNode, refDomNode);
}
vnode.storeId();
vnode._moveToParent(vParent, index+i);
}
}
},
/**
* Gets or sets the innerContent of the Node as plain text.
*
* The setter syncs with the DOM.
*
* @property textContent
* @type String
* @since 0.0.1
*/
textContent: {
get: function() {
var instance = this,
text = '',
vChildNodes = instance.vChildNodes,
len, i, vChildNode;
if (instance.nodeType===1) {
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
text += (vChildNode.nodeType===3) ? vChildNode.text : ((vChildNode.nodeType===1) ? vChildNode.textContent : '');
}
}
else {
text = instance.text;
}
return text;
},
set: function(v) {
var vnode = Object.create(vNodeProto);
vnode.domNode = DOCUMENT.createTextNode(v);
// create circular reference:
vnode.domNode._vnode = vnode;
vnode.nodeType = 3;
vnode.text = vnode.domNode.textContent;
this._setChildNodes([vnode]);
}
},
/**
* Hash with all the children (vnodes). vChildren are vnodes that have a representing dom-node that is an HtmlElement (nodeType===1)
*
* @property vChildren
* @type Array
* @since 0.0.1
*/
vChildren: {
get: function() {
var instance = this,
children = instance._vChildren,
vChildNode, vChildNodes, i, len;
vChildNodes = instance.vChildNodes;
if (vChildNodes && !children) {
children = instance._vChildren = [];
len = vChildNodes.length;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
(vChildNode.nodeType===1) && (children[children.length]=vChildNode);
}
}
children || (children = instance._vChildren = []);
return children;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirst
* @type vnode
* @since 0.0.1
*/
vFirst: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstChild;
}
},
/**
* Reference to the first vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirstChild
* @type vnode
* @since 0.0.1
*/
vFirstChild: {
get: function() {
return (this.vChildNodes && this.vChildNodes[0]) || null;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vFirstElement
* @type vnode
* @since 0.0.1
*/
vFirstElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstElementChild;
}
},
/**
* Reference to the first vChild, where the related dom-node an Element (nodeType===1).
*
* @property vFirstElementChild
* @type vnode
* @since 0.0.1
*/
vFirstElementChild: {
get: function() {
return this.vChildren[0] || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLast
* @type vnode
* @since 0.0.1
*/
vLast: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastChild;
}
},
/**
* Reference to the last vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLastChild
* @type vnode
* @since 0.0.1
*/
vLastChild: {
get: function() {
var vChildNodes = this.vChildNodes;
return (vChildNodes && vChildNodes[vChildNodes.length-1]) || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vLastElement
* @type vnode
* @since 0.0.1
*/
vLastElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastElementChild;
}
},
/**
* Reference to the last vChild, where the related dom-node an Element (nodeType===1).
*
* @property vLastElementChild
* @type vnode
* @since 0.0.1
*/
vLastElementChild: {
get: function() {
var vChildren = this.vChildren;
return vChildren[vChildren.length-1] || null;
}
},
/**
* the Parent vnode
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
/**
* Reference to the next of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vNext
* @type vnode
* @since 0.0.1
*/
vNext: {
get: function() {
return _findNodeSibling(this, true);
}
},
/**
* Reference to the next of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vNextElement
* @type vnode
* @since 0.0.1
*/
vNextElement: {
get: function() {
return _findElementSibling(this, true);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vPrevious
* @type vnode
* @since 0.0.1
*/
vPrevious: {
get: function() {
return _findNodeSibling(this);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vPreviousElement
* @type vnode
* @since 0.0.1
*/
vPreviousElement: {
get: function() {
return _findElementSibling(this);
}
}
});
return vNodeProto;
};
},{"./attribute-extractor.js":2637,"./html-parser.js":2641,"./vdom-ns.js":2643,"js-ext/extra/hashmap.js":2595,"js-ext/extra/lightmap.js":2596,"js-ext/lib/array.js":2597,"js-ext/lib/object.js":2598,"js-ext/lib/string.js":2600,"polyfill":2617,"utils/lib/timers.js":2620}],2645:[function(require,module,exports){
arguments[4][100][0].apply(exports,arguments)
},{"./partials/extend-document.js":2639,"./partials/extend-element.js":2640,"./partials/node-parser.js":2642,"js-ext/extra/hashmap.js":2595,"js-ext/lib/object.js":2598,"utils/lib/timers.js":2620}],2646:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":2647}],2647:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":2648,"js-ext/lib/object.js":2649}],2648:[function(require,module,exports){
module.exports=require(4)
},{}],2649:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2648,"polyfill/polyfill-base.js":2652,"utils":2653}],2650:[function(require,module,exports){
module.exports=require(14)
},{}],2651:[function(require,module,exports){
module.exports=require(15)
},{}],2652:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2650,"./lib/window.console.js":2651}],2653:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2654,"./lib/timers.js":2655}],2654:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2648,"polyfill/polyfill-base.js":2658}],2655:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2658}],2656:[function(require,module,exports){
module.exports=require(14)
},{}],2657:[function(require,module,exports){
module.exports=require(15)
},{}],2658:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2656,"./lib/window.console.js":2657}],2659:[function(require,module,exports){
"use strict";
/**
* Integrates DOM-events to event. more about DOM-events:
* http://www.smashingmagazine.com/2013/11/12/an-introduction-to-dom-events/
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @example
* Event = require('event-dom')(window);
*
* @module event
* @submodule event-dom
* @class Event
* @since 0.0.1
*/
var NAME = '[event-dom]: ',
Event = require('event'),
later = require('utils').later,
createHashMap = require('js-ext/extra/hashmap.js').createMap,
OUTSIDE = 'outside',
REGEXP_NODE_ID = /^#\S+$/,
REGEXP_EXTRACT_NODE_ID = /#(\S+)/,
REGEXP_UI_OUTSIDE = /^.+outside$/,
TIME_BTN_PRESSED = 200,
PURE_BUTTON_ACTIVE = 'pure-button-active',
UI = 'UI:',
NODE = 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
TAP = 'tap',
CLICK = 'click',
RIGHTCLICK = 'right'+CLICK,
CENTERCLICK = 'center'+CLICK,
EV_REMOVED = UI+NODE+REMOVE,
EV_INSERTED = UI+NODE+INSERT,
EV_CONTENT_CHANGE = UI+NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = UI+ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = UI+ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = UI+ATTRIBUTE+INSERT,
mutationEventsDefined = false,
NO_DEEP_SEARCH = {},
/*
* Internal hash containing all DOM-events that are listened for (at `document`).
*
* DOMEvents = {
* 'tap': callbackFn,
* 'mousemove': callbackFn,
* 'keypress': callbackFn
* }
*
* @property DOMEvents
* @default {}
* @type Object
* @private
* @since 0.0.1
*/
DOMEvents = {};
require('js-ext/lib/string.js');
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('polyfill/polyfill-base.js');
module.exports = function (window) {
var DOCUMENT = window.document,
_domSelToFunc, _evCallback, _findCurrentTargets, _preProcessor, _setupEvents, _setupMutationListener, _teardownMutationListener,
_setupDomListener, _teardownDomListener, SORT, _sortFunc, _sortFuncReversed, _getSubscribers, _selToFunc, MUTATION_EVENTS, preventClick;
require('vdom')(window);
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.EventDom) {
return Event; // Event was already extended
}
console.warn('event-dom 9');
MUTATION_EVENTS = [EV_REMOVED, EV_INSERTED, EV_CONTENT_CHANGE, EV_ATTRIBUTE_REMOVED, , EV_ATTRIBUTE_CHANGED, EV_ATTRIBUTE_INSERTED];
/*
* Transforms the selector to a valid function
*
* @method _selToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_selToFunc = function(customEvent, subscriber) {
Event._sellist.some(function(selFn) {
return selFn(customEvent, subscriber);
});
};
/*
* Creates a filterfunction out of a css-selector. To be used for catching any dom-element, without restrictions
* of any context (like Parcels can --> Parcel.Event uses _parcelSelToDom instead)
* On "non-outside" events, subscriber.t is set to the node that first matches the selector
* so it can be used to set as e.target in the final subscriber
*
* @method _domSelToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_domSelToFunc = function(customEvent, subscriber) {
// this stage is runned during subscription
var outsideEvent = REGEXP_UI_OUTSIDE.test(customEvent),
selector = subscriber.f,
context = subscriber.o,
vnode = subscriber.o.vnode,
isCustomElement = vnode && vnode.isItag,
visibleContent = isCustomElement && !vnode.domNode.contentHidden,
nodeid, byExactId, newTarget, deepSearch;
isCustomElement = false;
console.log(NAME, '_domSelToFunc type of selector = '+typeof selector);
// note: selector could still be a function: in case another subscriber
// already changed it.
if ((!selector && !isCustomElement) || (typeof selector === 'function')) {
subscriber.n || (subscriber.n = isCustomElement ? context : DOCUMENT);
return true;
}
selector || (selector='');
nodeid = selector.match(REGEXP_EXTRACT_NODE_ID);
nodeid ? (subscriber.nId=nodeid[1]) : (subscriber.n=isCustomElement ? context : DOCUMENT);
byExactId = REGEXP_NODE_ID.test(selector);
deepSearch = !NO_DEEP_SEARCH[customEvent];
// set the selector to `subscriber._s` so that e.currentTarget can calculate it:
subscriber._s = selector;
subscriber.f = function(e) {
// this stage is runned when the event happens
console.log(NAME, '_domSelToFunc inside filter. selector: '+selector);
var node = e.target,
vnode = node.vnode,
character1 = selector && selector.substr(1),
match = false;
if (!isCustomElement || visibleContent || context.contains(node)) {
if (selector==='') {
match = true;
}
else {
// e.target is the most deeply node in the dom-tree that caught the event
// our listener uses `selector` which might be a node higher up the tree.
// we will reset e.target to this node (if there is a match)
// note that e.currentTarget will always be `document` --> we're not interested in that
// also, we don't check for `node`, but for node.matchesSelector: the highest level `document`
// is not null, yet it doesn't have .matchesSelector so it would fail
// we DON'T want this for the focus and blur event!
if (deepSearch) {
if (vnode) {
// we go through the vdom
if (!vnode.removedFromDOM) {
while (vnode && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = vnode.domNode;
}
vnode = vnode.vParent;
}
}
}
else {
// we go through the dom
while (node.matchesSelector && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the dom');
match = byExactId ? (node.id===character1) : node.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = node;
}
node = node.parentNode;
}
}
}
else {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
if (vnode) {
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
}
else {
match = node.matchesSelector && (byExactId ? (node.id===character1) : node.matchesSelector(selector));
}
match && (e.sourceTarget=node);
}
}
}
if (outsideEvent && !match) {
// there is a match for the outside-event:
// we need to set e.sourceTarget and e.target:
newTarget = DOCUMENT.getElement(selector, true);
if (newTarget) {
e.sourceTarget = node;
subscriber.t = newTarget;
}
else {
// make return `false` because the selector is not in the dom
match = true;
}
}
console.log(NAME, '_domSelToFunc filter returns '+(!outsideEvent ? match : !match));
return !outsideEvent ? match : !match;
};
return true;
};
// at this point, we need to find out what are the current node-refs. whenever there is
// a filter that starts with `#` --> in those cases we have a bubble-chain, because the selector isn't
// set up with `document` at its root.
// we couldn't do this at time of subscribtion, for the nodes might not be there at that time.
// however, we only need to do this once: we store the value if we find them
// no problem when the nodes leave the dom later: the previous filter wouldn't pass
_findCurrentTargets = function(subscribers) {
console.log(NAME, '_findCurrentTargets');
subscribers.forEach(
function(subscriber) {
console.log(NAME, '_findCurrentTargets for single subscriber. nId: '+subscriber.nId);
subscriber.nId && (subscriber.n=DOCUMENT.getElementById(subscriber.nId, true));
}
);
};
/*
* Generates an event through our Event-system. Does the actual transportation from DOM-events
* into our Eventsystem. It also looks at the response of our Eventsystem: if our system
* halts or preventDefaults the customEvent, then the original DOM-event will be preventDefaulted.
*
* @method _evCallback
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_evCallback = function(e) {
console.log(NAME, '_evCallback');
var allSubscribers = Event._subs,
eType = e.type,
eventobject, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs, subsOutside,
subscribers, eventobjectOutside, wildcard_named_subsOutside, customEvent, eventName, which;
eventName = eType;
// first: a `click` event might be needed to transformed into `rightclick`:
if (eventName===CLICK) {
which = e.which;
(which===2) && (eventName=CENTERCLICK);
(which===3) && (eventName=RIGHTCLICK);
}
if (eventName==='tap') {
// prevent the next click-event
preventClick = true;
e.clientX || (e.clientX = e.center && e.center.x);
e.clientY || (e.clientY = e.center && e.center.y);
}
else if (preventClick && (eventName===CLICK)) {
preventClick = false;
return;
}
if (eventName===CLICK) {
eventName = 'tap';
e.center = {
x: e.clientX,
y: e.clientY
};
e.eventType = 4;
e.pointerType = 'mouse';
e.tapCount = 1;
}
customEvent = 'UI:'+eventName;
subs = allSubscribers[customEvent];
wildcard_named_subs = allSubscribers['*:'+eventName];
named_wildcard_subs = allSubscribers['UI:*'];
wildcard_wildcard_subs = allSubscribers['*:*'];
// Emit the dom-event though our eventsystem:
// NOTE: emit() needs to be synchronous! otherwise we wouldn't be able
// to preventDefault in time
//
// e = eventobject from the DOM-event OR gesture-event
// eventobject = eventobject from our Eventsystem, which get returned by calling `emit()`
// now so the work:
subscribers = _getSubscribers(e, true, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
eventobject = Event._emit(e.target, customEvent, e, subscribers, [], _preProcessor, false, true);
// now check outside subscribers
subsOutside = allSubscribers[customEvent+OUTSIDE];
wildcard_named_subsOutside = allSubscribers['*:'+eventName+OUTSIDE];
subscribers = _getSubscribers(e, true, subsOutside, wildcard_named_subsOutside);
eventobjectOutside = Event._emit(e.target, customEvent+OUTSIDE, e, subscribers, [], _preProcessor, false, true);
// if eventobject was preventdefaulted or halted: take appropriate action on
// the original dom-event. Note: only the original event can caused this, not the outsideevent
// stopPropagation on the original eventobject has no impact on our eventsystem, but who know who else is watching...
// be carefull though: not all gesture events have e.stopPropagation
eventobject.status.halted && e.stopPropagation && e.stopPropagation();
// now we might need to preventDefault the original event.
// be carefull though: not all gesture events have e.preventDefault
if ((eventobject.status.halted || eventobject.status.defaultPrevented || eventobject.status.defaultPreventedContinue) && e.preventDefault) {
e.preventDefault();
}
if (eventobject.status.ok) {
// last step: invoke the aftersubscribers
// we need to do this asynchronous: this way we pass them AFTER the DOM-event's defaultFn
// also make sure to paas-in the payload of the manipulated eventobject
subscribers = _getSubscribers(e, false, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent, eventobject, [], subscribers, _preProcessor, true), 10);
// now check outside subscribers
subscribers = _getSubscribers(e, false, subsOutside, wildcard_named_subsOutside);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent+OUTSIDE, eventobjectOutside, [], subscribers, _preProcessor, true), 10);
}
};
/*
* Creates an array of subscribers in the right order, conform their position in the DOM.
* Only subscribers that match the filter are involved.
*
* @method _getSubscribers
* @param e {Object} eventobject
* @param before {Boolean} whether it is a before- or after-subscriber
* @param subs {Array} array with subscribers
* @param wildcard_named_subs {Array} array with subscribers
* @param named_wildcard_subs {Array} array with subscribers
* @param wildcard_wildcard_subs {Array} array with subscribers
* @private
* @since 0.0.1
*/
_getSubscribers = function(e, before, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs) {
var subscribers = [],
beforeOrAfter = before ? 'b' : 'a',
saveConcat = function(extrasubs) {
extrasubs && extrasubs[beforeOrAfter] && (subscribers=subscribers.concat(extrasubs[beforeOrAfter]));
};
saveConcat(subs);
saveConcat(wildcard_named_subs);
saveConcat(named_wildcard_subs);
saveConcat(wildcard_wildcard_subs);
if (subscribers.length>0) {
subscribers = function(array, testFunc) {
// quickest way to filter an array: see http://jsperf.com/array-filter-performance/4
var filtered = array.slice(0), i;
for (i=array.length-1; i>=0; i--) {
console.log(NAME, 'filtercheck for subscriber');
testFunc(array[i]) || filtered.splice(i, 1);
}
return filtered;
}(subscribers, function(subscriber) {return (!subscriber.f || subscriber.f.call(subscriber.o, e));});
if (subscribers.length>0) {
// _findCurrentTargets(subscribers);
// sorting, based upon the sortFn
subscribers.sort(SORT);
}
}
return subscribers;
};
/*
* Sets e.target and e.sourceTarget for the single subscriber.
* Needs to be done for evenry single subscriber, because with a single event, these values change for each subscriber
*
* @method _preProcessor
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_preProcessor = function(subscriber, e) {
console.log(NAME, '_preProcessor');
// inside the aftersubscribers, we may need exit right away.
// this would be the case whenever stopPropagation or stopImmediatePropagation was called
// in case the subscribernode equals the node on which stopImmediatePropagation was called: return true
var propagationStopped, immediatePropagationStopped,
targetnode = (subscriber.t || subscriber.n);
immediatePropagationStopped = e.status.immediatePropagationStopped;
if (immediatePropagationStopped && ((immediatePropagationStopped===targetnode) || !immediatePropagationStopped.contains(targetnode))) {
console.log(NAME, '_preProcessor will return true because of immediatePropagationStopped');
return true;
}
// in case the subscribernode does not fall within or equals the node on which stopPropagation was called: return true
propagationStopped = e.status.propagationStopped;
if (propagationStopped && (propagationStopped!==targetnode) && !propagationStopped.contains(targetnode)) {
console.log(NAME, '_preProcessor will return true because of propagationStopped');
return true;
}
e._s = subscriber._s;
// now we might need to set e.target to the right node:
// the filterfunction might have found the true domnode that should act as e.target
// and set it at subscriber.t
// also, we need to backup the original e.target: this one should be reset when
// we encounter a subscriber with its own filterfunction instead of selector
if (subscriber.t) {
e.sourceTarget || (e.sourceTarget=e.target);
e.target = subscriber.t;
}
else {
e.sourceTarget && (e.target=e.sourceTarget);
}
return false;
};
/*
* Transports DOM-events to the Event-system. Catches events at their most early stage:
* their capture-phase. When these events happen, a new customEvent is generated by our own
* Eventsystem, by calling _evCallback(). This way we keep DOM-events and our Eventsystem completely separated.
*
* @method _setupDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_setupDomListener = function(customEvent, subscriber) {
console.log(NAME, '_setupDomListener');
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0],
eventName = eventSplitted[1],
outsideEvent = REGEXP_UI_OUTSIDE.test(eventName);
// be careful: anyone could also register an `outside`-event.
// in those cases, the DOM-listener must be set up without `outside`
outsideEvent && (eventName=eventName.substring(0, eventName.length-7));
// if eventName equals `mouseover` or `mouseleave` then we quit:
// people should use `mouseover` and `mouseout`
if ((eventName==='mouseenter') || (eventName==='mouseleave')) {
console.warn(NAME, 'Subscription to '+eventName+' not supported, use mouseover and mouseout: this eventsystem uses these non-noisy so they act as mouseenter and mouseleave');
return;
}
// only accept tap-events, yet later on we WILL need to listen for click-events
(eventName===CLICK) && (eventName=TAP);
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(emitterName+':'+eventName+(outsideEvent ? OUTSIDE : ''), subscriber);
// already registered? then return, also return if someone registered for UI:*
if (DOMEvents[eventName] || (eventName==='*')) {
// cautious: one might have registered the event, but not yet the outsideevent.
// in that case: save this setting:
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
return;
}
DOMEvents[eventName] = true;
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
// one exception: windowresize should listen to the window-object
if (eventName==='resize') {
window.addEventListener(eventName, _evCallback);
}
else {
((eventName===RIGHTCLICK) || (eventName===CENTERCLICK)) && (eventName=TAP);
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.addEventListener(eventName, _evCallback, true);
// listen for both `tap` and `click` events to happen
(eventName===TAP) && DOCUMENT.addEventListener(CLICK, _evCallback, true);
}
};
_setupEvents = function() {
var lastFocussed;
// make sure disabled buttons don't work:
Event.before(['tap', 'press'], function(e) {
e.preventDefault();
}, '.pure-button-disabled, button[disabled]');
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.before(
'keydown',
function(e) {
e._buttonPressed = true;
Event.emit(e.target, 'UI:tap', e);
},
function(e) {
var keyCode = e.keyCode;
return (e.target.getTagName()==='BUTTON') && ((keyCode===13) || (keyCode===32));
}
);
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.after(
'tap',
function(e) {
var buttonNode = e.target;
if (e._buttonPressed) {
buttonNode.setClass(PURE_BUTTON_ACTIVE);
e._noRender = true;
// even if the node isn't in the DOM, we can still try to manipulate it:
// the vdom makes sure no errors occur when the node is already removed
later(buttonNode.removeClass.bind(buttonNode, PURE_BUTTON_ACTIVE), TIME_BTN_PRESSED);
}
}
);
// fix activeElement on Mac
Event.before('focus', function(e) {
// will come here more often because of bubblechain
// however, the last pass-through will set the deepest node
lastFocussed = e.target;
});
// patching DOCUMENT.activeElement because it doesn't work well in a Mac: https://developer.mozilla.org/en-US/docs/Web/API/document.activeElement
// DOCUMENT._activeElement is used with the patch for DOCUMENT.activeElement its getter
Event.after('focus', function() {
DOCUMENT._activeElement = lastFocussed;
});
Event.before('blur', function() {
DOCUMENT._activeElement = null;
});
// Note: window.document has no prototype
Object.defineProperty(DOCUMENT, 'activeElement', {
get: function() {
return DOCUMENT._activeElement || DOCUMENT.body;
}
});
};
_setupMutationListener = function() {
DOCUMENT.hasMutationSubs = true;
if (!mutationEventsDefined) {
Event.defineEvent(EV_REMOVED).unPreventable();
Event.defineEvent(EV_INSERTED).unPreventable();
Event.defineEvent(EV_CONTENT_CHANGE).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_REMOVED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_CHANGED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_INSERTED).unPreventable();
mutationEventsDefined = true;
}
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFunc = function(subscriberOne, subscriberTwo) {
return (subscriberTwo.t || subscriberTwo.n).contains(subscriberOne.t || subscriberOne.n) ? -1 : 1;
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFuncReversed = function(subscriberOne, subscriberTwo) {
return (subscriberOne.t || subscriberOne.n).contains(subscriberTwo.t || subscriberTwo.n) ? 1 : -1;
};
/*
* Removes DOM-eventsubscribers from document when they are no longer needed.
*
* @method _teardownDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @private
* @since 0.0.2
*/
_teardownDomListener = function(customEvent) {
var customEventWithoutOutside = customEvent.endsWith(OUTSIDE) ? customEvent.substr(0, customEvent.length-7) : customEvent,
eventSplitted = customEventWithoutOutside.split(':'),
eventName = eventSplitted[1],
stillInUse;
if ((customEventWithoutOutside===CLICK) || (customEventWithoutOutside===RIGHTCLICK) || (customEventWithoutOutside===CENTERCLICK)) {
stillInUse = Event._subs[CLICK] ||
Event._subs[CLICK+OUTSIDE];
Event._subs[RIGHTCLICK] ||
Event._subs[RIGHTCLICK+OUTSIDE],
Event._subs[CENTERCLICK] ||
Event._subs[CENTERCLICK+OUTSIDE];
eventName = CLICK;
}
else {
stillInUse = Event._subs[customEventWithoutOutside] || Event._subs[customEventWithoutOutside+OUTSIDE];
}
if (!stillInUse) {
console.log(NAME, '_teardownDomListener '+customEvent);
// remove eventlistener from `document`
// one exeption: windowresize should listen to the window-object
if (eventName==='resize') {
window.removeEventListener(eventName, _evCallback);
}
else {
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.removeEventListener(eventName, _evCallback, true);
}
delete DOMEvents[eventName];
}
};
_teardownMutationListener = function() {
if (!Event._subs[EV_REMOVED] &&
!Event._subs[EV_INSERTED] &&
!Event._subs[EV_CONTENT_CHANGE] &&
!Event._subs[EV_ATTRIBUTE_REMOVED] &&
!Event._subs[EV_ATTRIBUTE_CHANGED] &&
!Event._subs[EV_ATTRIBUTE_INSERTED]
) {
DOCUMENT.hasMutationSubs = false;
}
};
// Now a very tricky one:
// Some browsers do an array.sort down-top instead of top-down.
// In those cases we need another sortFn, for the position on an equal match should fall
// behind instead of before (which is the case on top-down sort)
[1,2].sort(function(a /*, b */) {
SORT || (SORT=(a===2) ? _sortFuncReversed : _sortFunc);
});
// Now we do some initialization in order to make DOM-events work:
Object.defineProperty(Event._defaultEventObj, 'currentTarget', {
get: function() {
var ev = this,
e_target = ev.target;
if (e_target && ev._s) {
if (e_target.matches(ev._s)) {
return e_target;
}
return e_target.inside(ev._s) || undefined;
}
}
});
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(UI+'*', _setupDomListener, Event)
._setEventObjProperty('stopPropagation', function() {this.status.ok || (this.status.propagationStopped = this.target);})
._setEventObjProperty('stopImmediatePropagation', function() {this.status.ok || (this.status.immediatePropagationStopped = this.target);});
// Notify when someone subscribes to any event at all --> we might need to transform the filterFn from a selector into a true fnction
// this is already done automaticly by _setupDomListener fo UI:* events
Event.notify('*:*', function(customEvent, subscriber) {
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0];
if ((emitterName!=='UI') && (typeof subscriber.f==='string')) {
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(customEvent, subscriber);
}
}, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(UI+'*', _teardownDomListener, Event);
Event._sellist = [_domSelToFunc];
_setupEvents();
// making HTMLElement to be able to emit using event-emitter:
(function(HTMLElementPrototype) {
HTMLElementPrototype.merge(Event.Emitter('UI'));
}(window.HTMLElement.prototype));
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(MUTATION_EVENTS, _setupMutationListener, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(MUTATION_EVENTS, _teardownMutationListener, Event);
// Note: window.document has no prototype
DOCUMENT.suppressMutationEvents = function(suppress) {
this._suppressMutationEvents = suppress;
};
// Event.noDeepDomEvt and Event._domCallback are the only method that is added to Event.
// We need to do this, because `event-mobile` needs access to the same method.
// We could have done without this method and instead listen for a custom-event to handle
// Mobile events, however, that would lead into 2 eventcycli which isn't performant.
/**
*
* @method noDeepDomEvt
* @param domEvent {String} the eventName that should be processed without deepsearch
* @param e {Object} eventobject
* @for Event
* @chainable
* @since 0.0.1
*/
Event.noDeepDomEvt = function(domEvent) {
domEvent.contains(':') || (domEvent=UI+domEvent);
NO_DEEP_SEARCH[domEvent] = true;
return this;
};
/**
* Does the actual transportation from DOM-events into the Eventsystem. It also looks at the response of
* the Eventsystem: on e.halt() or e.preventDefault(), the original DOM-event will be preventDefaulted.
*
* @method _domCallback
* @param eventName {String} the customEvent that is transported to the eventsystem
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
Event._domCallback = function(e) {
_evCallback(e);
};
// store module:
window._ITSAmodules.EventDom = Event;
return Event;
};
},{"event":2667,"js-ext/extra/hashmap.js":2683,"js-ext/lib/array.js":2684,"js-ext/lib/object.js":2685,"js-ext/lib/string.js":2686,"polyfill/polyfill-base.js":2698,"utils":2699,"vdom":2757}],2660:[function(require,module,exports){
"use strict";
/**
* Adds the `blurnode` event as a DOM-event to event-dom. more about DOM-events:
* http://www.smashingmagazine.com/2013/11/12/an-introduction-to-dom-events/
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @example
* Event = require('event-dom/blurnode.js')(window);
*
* or
*
* @example
* Event = require('event-dom')(window);
* require('event-dom/event-blurnode.js')(window);
*
* @module event
* @submodule event-blurnode
* @class Event
* @since 0.0.2
*/
var NAME = '[event-blurnode]: ',
createHashMap = require('js-ext/extra/hashmap.js').createMap;
require('js-ext/lib/object.js');
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.EventBlurNode) {
return window._ITSAmodules.EventBlurNode; // EventBlurNode was already created
}
var Event = require('../event-dom.js')(window),
blurVNode, subscriber, focusEvent,
/*
* Creates the `blurnode` event.
*
* @method setupBlurNode
* @private
* @since 0.0.2
*/
setupBlurNode = function() {
// create only after subscribing to the `hover`-event
subscriber = Event.before('blur', function(e) {
console.log(NAME, 'making list of blur-nodes');
blurVNode || (blurVNode=e.target.vnode);
focusEvent || (focusEvent=Event.onceAfter('focus', function(e2) {
var focusVNode = e2.target.vnode;
while (focusVNode && blurVNode && !blurVNode.contains(focusVNode)) {
Event.emit(blurVNode.domNode, 'UI:blurnode', e);
blurVNode = blurVNode.vParent;
}
blurVNode = null;
focusEvent = null;
}));
});
},
/*
* Removes the `blurnode` event. Because there are no subscribers anymore.
*
* @method teardownBlurNode
* @private
* @since 0.0.2
*/
teardownBlurNode = function() {
// check if there aren't any subscribers anymore.
// in that case, we detach the `mouseover` lister because we don't want to
// loose performance.
if (!Event._subs['UI:blurnode']) {
console.log(NAME, 'teardownBlurNode: stop listening for blur-event');
subscriber.detach();
// reinit notifier, because it is a one-time notifier:
Event.notify('UI:blurnode', setupBlurNode, Event, true);
}
};
Event.defineEvent('UI:blurnode').unPreventable();
Event.notify('UI:blurnode', setupBlurNode, Event, true);
Event.notifyDetach('UI:blurnode', teardownBlurNode, Event);
Event.noDeepDomEvt('UI:blurnode');
window._ITSAmodules.EventBlurNode = Event;
return Event;
};
},{"../event-dom.js":2659,"js-ext/extra/hashmap.js":2683,"js-ext/lib/object.js":2685}],2661:[function(require,module,exports){
"use strict";
/**
* Adds the `focusnode` event as a DOM-event to event-dom. more about DOM-events:
* http://www.smashingmagazine.com/2013/11/12/an-introduction-to-dom-events/
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @example
* Event = require('event-dom/focusnode.js')(window);
*
* or
*
* @example
* Event = require('event-dom')(window);
* require('event-dom/event-focusnode.js')(window);
*
* @module event
* @submodule event-focusnode
* @class Event
* @since 0.0.2
*/
var NAME = '[event-focusnode]: ',
createHashMap = require('js-ext/extra/hashmap.js').createMap;
require('js-ext/lib/object.js');
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.EventFocusNode) {
return window._ITSAmodules.EventFocusNode; // EventFocusNode was already created
}
var Event = require('../event-dom.js')(window),
subscriber, previousVnode,
/*
* Creates the `focisnode` event.
*
* @method setupFocusNode
* @private
* @since 0.0.2
*/
setupFocusNode = function() {
// create only after subscribing to the `hover`-event
subscriber = Event.after('focus', function(e) {
var targetVnode = e.target.vnode,
vnode = targetVnode,
alreadyFocussed;
if (vnode) {
do {
alreadyFocussed = previousVnode && vnode.contains(previousVnode);
alreadyFocussed || Event.emit(vnode.domNode, 'UI:focusnode', e);
/*jshint boss:true */
} while (!alreadyFocussed && (vnode=vnode.vParent));
/*jshint boss:false */
}
previousVnode = targetVnode;
});
},
/*
* Removes the `focusnode` event. Because there are no subscribers anymore.
*
* @method teardownFocusNode
* @private
* @since 0.0.2
*/
teardownFocusNode = function() {
// check if there aren't any subscribers anymore.
// in that case, we detach the `mouseover` lister because we don't want to
// loose performance.
if (!Event._subs['UI:focusnode']) {
console.log(NAME, 'teardownFocusNode: stop listening for blur-event');
subscriber.detach();
// reinit notifier, because it is a one-time notifier:
Event.notify('UI:focusnode', setupFocusNode, Event, true);
}
};
Event.defineEvent('UI:focusnode').unPreventable();
Event.notify('UI:focusnode', setupFocusNode, Event, true);
Event.notifyDetach('UI:focusnode', teardownFocusNode, Event);
Event.noDeepDomEvt('UI:focusnode');
window._ITSAmodules.EventFocusNode = Event;
return Event;
};
},{"../event-dom.js":2659,"js-ext/extra/hashmap.js":2683,"js-ext/lib/object.js":2685}],2662:[function(require,module,exports){
"use strict";
/**
* Adds the `hover` event as a DOM-event to event-dom. more about DOM-events:
* http://www.smashingmagazine.com/2013/11/12/an-introduction-to-dom-events/
*
* Should be called using the provided `mergeInto`-method like this:
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @example
* Event = require('event-dom/hover.js')(window);
*
* or
*
* @example
* Event = require('event-dom')(window);
* require('event-dom/event-hover.js')(window);
*
* @module event
* @submodule event-hover
* @class Event
* @since 0.0.2
*/
var NAME = '[event-hover]: ',
createHashMap = require('js-ext/extra/hashmap.js').createMap;
require('js-ext/lib/object.js');
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.EventHover) {
return window._ITSAmodules.EventHover; // EventHover was already created
}
var Event = require('../event-dom.js')(window),
subscriber,
/*
* Creates the `hover` event. The eventobject has the property `e.hover` which is a `Promise`.
* You can use this Promise to get notification of the end of hover. The Promise e.hover gets resolved with
* `relatedTarget` as argument: the node where the mouse went into when leaving a.target.
*
* @method setupHover
* @private
* @since 0.0.2
*/
setupHover = function() {
// create only after subscribing to the `hover`-event
subscriber = Event.after('mouseover', function(e) {
console.log(NAME, 'setupHover: setting up mouseover event');
var node = e.target;
e.hover = new Promise(function(fulfill) {
Event.onceAfter(
'mouseout',
function(e) {
fulfill(e.relatedTarget);
},
function(ev) {
return (ev.target===node);
}
);
});
Event.emit(node, 'UI:hover', e);
});
},
/*
* Removes the `hover` event. Because there are no subscribers anymore.
*
* @method teardownHover
* @private
* @since 0.0.2
*/
teardownHover = function() {
// check if there aren't any subscribers anymore.
// in that case, we detach the `mouseover` lister because we don't want to
// loose performance.
if (!Event._subs['UI:hover']) {
console.log(NAME, 'teardownHover: stop setting up mouseover event');
subscriber.detach();
// reinit notifier, because it is a one-time notifier:
Event.notify('UI:hover', setupHover, Event, true);
}
};
Event.notify('UI:hover', setupHover, Event, true);
Event.notifyDetach('UI:hover', teardownHover, Event);
window._ITSAmodules.EventHover = Event;
return Event;
};
},{"../event-dom.js":2659,"js-ext/extra/hashmap.js":2683,"js-ext/lib/object.js":2685}],2663:[function(require,module,exports){
"use strict";
/**
* Adds the `hover` event as a DOM-event to event-dom. more about DOM-events:
* http://www.smashingmagazine.com/2013/11/12/an-introduction-to-dom-events/
*
* Should be called using the provided `mergeInto`-method like this:
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @example
* Event = require('event-dom/hover.js')(window);
*
* or
*
* @example
* Event = require('event-dom')(window);
* require('event-dom/event-hover.js')(window);
*
* @module event
* @submodule event-hover
* @class Event
* @since 0.0.2
*/
require('vdom');
require('js-ext/lib/object.js');
var NAME = '[event-valuechange]: ',
createHashMap = require('js-ext/extra/hashmap.js').createMap,
VALUE = 'value',
DATA_KEY = 'valueChange',
UTILS = require('utils'),
/**
* Interval (in milliseconds) at which to poll for changes to the value of an
* element with one or more `valuechange` subscribers, because of a `right-click paste`
* which cannot be determined by the event-system
*
* @property POLL_INTERVAL
* @type Number
* @default 250
* @static
**/
POLL_INTERVAL = 250;
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.EventValueChange) {
return window._ITSAmodules.EventValueChange; // EventValueChange was already created
}
var Event = require('../event-dom.js')(window),
DOCUMENT = window.document,
subscriberBlur,
subscriberFocus,
subscriberRemoval,
/*
* Checks if the HtmlElement is editable.
*
* @method editableNode
* @param node {HtmlElement}
* @private
* @return {Boolean} whether the HtmlElement is editable.
* @since 0.0.1
*/
editableNode = function(node) {
var editable;
if (node===DOCUMENT) {
return false;
}
console.log(NAME, 'editableNodes '+DOCUMENT.test(node, 'input, textarea, select') || ((editable=node.getAttr('contenteditable')) && (editable!=='false')));
return DOCUMENT.test(node, 'input, textarea, select') || ((editable=node.getAttr('contenteditable')) && (editable!=='false'));
},
/*
* Gets invokes when the HtmlElement gets focus. Initializes a `keypress` and `click`/'press' eventlisteners.
*
* @method startFocus
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
startFocus = function(e) {
console.log(NAME, 'startFocus');
var node = e.target,
editable, valueChangeData;
if (!editableNode(node)) {
return;
}
// first backup the current value:
editable = ((editable=node.getAttr('contenteditable')) && (editable!=='false'));
valueChangeData = node.getData(DATA_KEY);
if (!valueChangeData) {
valueChangeData = {
editable : editable
};
node.setData(DATA_KEY, valueChangeData);
}
valueChangeData.prevVal = editable ? node.innerHTML : node[VALUE];
// both next eventlisteners will detach inside their subscriber:
subscriberBlur = Event.after('blur', endFocus);
subscriberRemoval = Event.after(
'noderemove',
endFocus,
function(e2) {
return (e2.target===node);
}
);
startPolling(e);
},
/*
* Removes the `focus` and `blur` events and ends the polling - if running. Because there are no subscribers anymore.
*
* @method endFocus
* @private
* @since 0.0.1
*/
endFocus = function(e) {
console.log(NAME, 'endFocus');
stopPolling(e.target);
// because we could come here by 2 different events,
// we need to detach them both
subscriberBlur.detach();
subscriberRemoval.detach();
},
/*
* Creates the `focus` and `blur` events. Also invokes `startFocus` to do inititalization.
*
* @method setupValueChange
* @private
* @since 0.0.2
*/
setupValueChange = function() {
console.log(NAME, 'setupValueChange');
// create only after subscribing to the `hover`-event
subscriberFocus = Event.after('focus', startFocus);
startFocus({target: DOCUMENT.activeElement});
},
/*
* Starts polling in case of mouseclicks.
*
* @method startPolling
* @private
* @since 0.0.1
*/
startPolling = function(e) {
var node = e.target,
valueChangeData;
if (!editableNode(node)) {
return;
}
console.log(NAME, 'startPolling');
valueChangeData = node.getData(DATA_KEY);
// cancel previous timer: we don't want multiple timers:
valueChangeData._pollTimer && valueChangeData._pollTimer.cancel();
// setup a new timer:
valueChangeData._pollTimer = UTILS.later(checkChanged.bind(null, e), POLL_INTERVAL, true);
},
/*
* Stops polling on the specific HtmlElement
*
* @method stopPolling
* @param node {HtmlElement} the HtmlElement that should stop polling.
* @private
* @since 0.0.1
*/
stopPolling = function(node) {
console.log(NAME, 'stopPolling');
var valueChangeData;
if (node && node.getData) {
valueChangeData = node.getData(DATA_KEY);
valueChangeData && valueChangeData._pollTimer && valueChangeData._pollTimer.cancel();
}
},
/*
* Checks e.target if its value has changed. If so, it will fire the `valuechange`-event.
*
* @method checkChanged
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
checkChanged = function(e) {
console.log(NAME, 'checkChanged');
var node = e.target;
// because of delegating all matched HtmlElements come along: only check the node that has focus:
if (DOCUMENT.activeElement!==node) {
return;
}
var prevData = node.getData(DATA_KEY),
editable = ((editable=node.getAttr('contenteditable')) && (editable!=='false')),
currentData = editable ? node.innerHTML : node[VALUE];
if (currentData!==prevData.prevVal) {
console.log(NAME, 'checkChanged --> value has been changed');
DOCUMENT._emitVC(node, currentData);
prevData.prevVal = currentData;
}
},
/*
* Removes the `focus` and `blur` events and ends the polling - if running. Because there are no subscribers anymore.
*
* @method teardownValueChange
* @private
* @since 0.0.1
*/
teardownValueChange = function() {
// check if there aren't any subscribers anymore.
// in that case, we detach the `mouseover` lister because we don't want to
// loose performance.
if (!Event._subs['UI:valuechange']) {
console.log(NAME, 'teardownValueChange: stop setting up blur and focus-event');
subscriberBlur && subscriberBlur.detach();
subscriberRemoval && subscriberRemoval.detach();
subscriberFocus.detach();
// also stop any possible action/listeners to a current element:
endFocus({target: DOCUMENT.activeElement});
// reinit notifier, because it is a one-time notifier:
Event.notify('UI:valuechange', setupValueChange, Event, true);
}
};
Event.defineEvent('UI:valuechange').unHaltable();
Event.notify('UI:valuechange', setupValueChange, Event, true);
Event.notifyDetach('UI:valuechange', teardownValueChange, Event);
/*
* Emits the `valuechange`-event on the specified node. Also adds e.value with the new value.
*
* @method _emitVC
* @param node {HtmlElement} the HtmlElement that fires the event
* @param value {String} the new value
* @private
* @since 0.0.1
*/
DOCUMENT._emitVC = function(node, value) {
console.log(NAME, 'document._emitVC');
var e = {
value: value
};
/**
* @event valuechange
* @param e.value {String} new value
* @param e.sourceTarget {Element} Element whare the valuechange occured
*/
Event.emit(node, 'UI:valuechange', e);
};
window._ITSAmodules.EventValueChange = Event;
return Event;
};
},{"../event-dom.js":2659,"js-ext/extra/hashmap.js":2683,"js-ext/lib/object.js":2685,"utils":2699,"vdom":2757}],2664:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":2669,"js-ext/lib/object.js":2670,"polyfill/polyfill-base.js":2682}],2665:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":2664}],2666:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":2664,"js-ext/extra/classes.js":2668,"js-ext/lib/object.js":2670}],2667:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":2664,"./event-emitter.js":2665,"./event-listener.js":2666}],2668:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":2670,"js-ext/extra/hashmap.js":2669,"polyfill/polyfill-base.js":2673}],2669:[function(require,module,exports){
module.exports=require(4)
},{}],2670:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2669,"polyfill/polyfill-base.js":2673,"utils":2674}],2671:[function(require,module,exports){
module.exports=require(14)
},{}],2672:[function(require,module,exports){
module.exports=require(15)
},{}],2673:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2671,"./lib/window.console.js":2672}],2674:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2675,"./lib/timers.js":2676}],2675:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2669,"polyfill/polyfill-base.js":2679}],2676:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2679}],2677:[function(require,module,exports){
module.exports=require(14)
},{}],2678:[function(require,module,exports){
module.exports=require(15)
},{}],2679:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2677,"./lib/window.console.js":2678}],2680:[function(require,module,exports){
module.exports=require(14)
},{}],2681:[function(require,module,exports){
module.exports=require(15)
},{}],2682:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2680,"./lib/window.console.js":2681}],2683:[function(require,module,exports){
module.exports=require(4)
},{}],2684:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":2689}],2685:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2683,"polyfill/polyfill-base.js":2689,"utils":2690}],2686:[function(require,module,exports){
module.exports=require(29)
},{}],2687:[function(require,module,exports){
module.exports=require(14)
},{}],2688:[function(require,module,exports){
module.exports=require(15)
},{}],2689:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2687,"./lib/window.console.js":2688}],2690:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2691,"./lib/timers.js":2692}],2691:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2683,"polyfill/polyfill-base.js":2695}],2692:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2695}],2693:[function(require,module,exports){
module.exports=require(14)
},{}],2694:[function(require,module,exports){
module.exports=require(15)
},{}],2695:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2693,"./lib/window.console.js":2694}],2696:[function(require,module,exports){
module.exports=require(14)
},{}],2697:[function(require,module,exports){
module.exports=require(15)
},{}],2698:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2696,"./lib/window.console.js":2697}],2699:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2700,"./lib/timers.js":2701}],2700:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2683,"polyfill/polyfill-base.js":2704}],2701:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2704}],2702:[function(require,module,exports){
module.exports=require(14)
},{}],2703:[function(require,module,exports){
module.exports=require(15)
},{}],2704:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2702,"./lib/window.console.js":2703}],2705:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],2706:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":2710,"js-ext/extra/hashmap.js":2707,"polyfill/polyfill-base.js":2716}],2707:[function(require,module,exports){
module.exports=require(4)
},{}],2708:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":2709,"../lib/object.js":2710,"./classes.js":2706,"js-ext/extra/hashmap.js":2707,"polyfill/lib/weakmap.js":2714}],2709:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":2716}],2710:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2707,"polyfill/polyfill-base.js":2716,"utils":2717}],2711:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":2716}],2712:[function(require,module,exports){
module.exports=require(29)
},{}],2713:[function(require,module,exports){
module.exports=require(14)
},{}],2714:[function(require,module,exports){
module.exports=require(57)
},{}],2715:[function(require,module,exports){
module.exports=require(15)
},{}],2716:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2713,"./lib/window.console.js":2715}],2717:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2718,"./lib/timers.js":2719}],2718:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2707,"polyfill/polyfill-base.js":2722}],2719:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2722}],2720:[function(require,module,exports){
module.exports=require(14)
},{}],2721:[function(require,module,exports){
module.exports=require(15)
},{}],2722:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2720,"./lib/window.console.js":2721}],2723:[function(require,module,exports){
module.exports=require(66)
},{}],2724:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":2723}],2725:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":2723}],2726:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":2723}],2727:[function(require,module,exports){
module.exports=require(14)
},{}],2728:[function(require,module,exports){
module.exports=require(15)
},{}],2729:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2727,"./lib/window.console.js":2728}],2730:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2731,"./lib/timers.js":2732}],2731:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2707,"polyfill/polyfill-base.js":2735}],2732:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2735}],2733:[function(require,module,exports){
module.exports=require(14)
},{}],2734:[function(require,module,exports){
module.exports=require(15)
},{}],2735:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2733,"./lib/window.console.js":2734}],2736:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":2737}],2737:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":2738,"js-ext/lib/object.js":2739}],2738:[function(require,module,exports){
module.exports=require(4)
},{}],2739:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2738,"polyfill/polyfill-base.js":2742,"utils":2743}],2740:[function(require,module,exports){
module.exports=require(14)
},{}],2741:[function(require,module,exports){
module.exports=require(15)
},{}],2742:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2740,"./lib/window.console.js":2741}],2743:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2744,"./lib/timers.js":2745}],2744:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2738,"polyfill/polyfill-base.js":2748}],2745:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2748}],2746:[function(require,module,exports){
module.exports=require(14)
},{}],2747:[function(require,module,exports){
module.exports=require(15)
},{}],2748:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2746,"./lib/window.console.js":2747}],2749:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":2707,"js-ext/lib/object.js":2710,"js-ext/lib/string.js":2712,"polyfill":2729,"polyfill/extra/transition.js":2724,"polyfill/extra/vendorCSS.js":2726}],2750:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":2707,"js-ext/lib/object.js":2710,"polyfill":2729}],2751:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":2707,"js-ext/lib/object.js":2710,"js-ext/lib/string.js":2712,"polyfill":2729}],2752:[function(require,module,exports){
arguments[4][95][0].apply(exports,arguments)
},{"../css/element.css":2705,"./attribute-extractor.js":2749,"./element-array.js":2750,"./html-parser.js":2753,"./node-parser.js":2754,"./vdom-ns.js":2755,"./vnode.js":2756,"js-ext/extra/hashmap.js":2707,"js-ext/lib/object.js":2710,"js-ext/lib/promise.js":2711,"js-ext/lib/string.js":2712,"polyfill":2729,"polyfill/extra/transition.js":2724,"polyfill/extra/transitionend.js":2725,"polyfill/extra/vendorCSS.js":2726,"utils":2730,"window-ext":2736}],2753:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":2749,"./vdom-ns.js":2755,"js-ext/extra/hashmap.js":2707,"js-ext/lib/object.js":2710,"polyfill":2729}],2754:[function(require,module,exports){
arguments[4][97][0].apply(exports,arguments)
},{"./attribute-extractor.js":2749,"./vdom-ns.js":2755,"./vnode.js":2756,"js-ext/extra/hashmap.js":2707,"js-ext/lib/object.js":2710,"polyfill":2729}],2755:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":2707,"js-ext/lib/object.js":2710,"polyfill":2729}],2756:[function(require,module,exports){
"use strict";
/**
* Delivers the `vnode` prototype object, which is a virtualisation of an `Element` inside the Dom.
* These Elements work smoothless with the vdom (see ...).
*
* vnodes are much quicker to access and walk through than native dom-nodes. However, this is a module you don't need
* by itself: `Element`-types use these features under the hood.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* <br>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
*
* @module vdom
* @submodule vnode
* @class vnode
* @since 0.0.1
*/
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('js-ext/lib/string.js');
require('polyfill');
var createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.VNode) {
return window._ITSAmodules.VNode; // VNODE was already created
}
console.warn('VDOM 31');
var NS = require('./vdom-ns.js')(window),
extractor = require('./attribute-extractor.js')(window),
DOCUMENT = window.document,
LightMap = require('js-ext/extra/lightmap.js'),
MUTATION_EVENTS = new LightMap(),
BATCH_WILL_RUN = false,
xmlNS = NS.xmlNS,
nodeids = NS.nodeids,
htmlToVNodes = require('./html-parser.js')(window),
timers = require('utils/lib/timers.js'),
async = timers.async,
later = timers.later,
/*jshint proto:true */
PROTO_SUPPORTED = !!Object.__proto__,
/*jshint proto:false */
// cleanup memory after 1 minute: removed nodes SHOULD NOT be accessed afterwards
// because vnode would be recalculated and might be different from before
DESTROY_DELAY = 60000,
unescapeEntities = NS.UnescapeEntities,
NTH_CHILD_REGEXP = /^(?:(\d*)[n|N])([\+|\-](\d+))?$/, // an+b
STRING = 'string',
CLASS = 'class',
STYLE = 'style',
ID = 'id',
NODE= 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
EV_REMOVED = NODE+REMOVE,
EV_INSERTED = NODE+INSERT,
EV_CONTENT_CHANGE = NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = ATTRIBUTE+INSERT,
SPLIT_CHARACTER = createHashMap({
' ': true,
'>': true,
'+': true, // only select the element when it is immediately preceded by the former element
'~': true // only the element when it has the former element as a sibling. (just like `+`, but less strict)
}),
STORABLE_SPLIT_CHARACTER = createHashMap({
'>': true,
'+': true,
'~': true
}),
SIBLING_MATCH_CHARACTER = createHashMap({
'+': true,
'~': true
}),
ATTR_DETAIL_SPECIFIERS = createHashMap({
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to attribute-name end-tokens.
*
* @property END_ATTRIBUTENAME
* @default {
* '=': true,
* ']': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
END_ATTRIBUTENAME = createHashMap({
'=': true,
']': true,
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to different changes of Element nodeType changes.
*
* @property NODESWITCH
* @default {
* 1: {
* 1: 1,
* 3: 2,
* 8: 3
* },
* 3: {
* 1: 4,
* 3: 5,
* 8: 6
* },
* 8: {
* 1: 7,
* 3: 8,
* 8: 9
* }
* }
* @type Object
* @protected
* @since 0.0.1
*/
NODESWITCH = createHashMap({
1: createHashMap({
1: 1, // oldNodeType==Element, newNodeType==Element
3: 2, // oldNodeType==Element, newNodeType==TextNode
8: 3 // oldNodeType==Element, newNodeType==Comment
}),
3: createHashMap({
1: 4, // oldNodeType==TextNode, newNodeType==Element
3: 5, // oldNodeType==TextNode, newNodeType==TextNode
8: 6 // oldNodeType==TextNode, newNodeType==Comment
}),
8: createHashMap({
1: 7, // oldNodeType==Comment, newNodeType==Element
3: 8, // oldNodeType==Comment, newNodeType==TextNode
8: 9 // oldNodeType==Comment, newNodeType==Comment
})
}),
/**
* Object to gain quick access to selector start-tokens.
*
* @property SELECTOR_IDENTIFIERS
* @default {
* '#': 1,
* '.': 2,
* '[': 3
* }
* @type Object
* @protected
* @since 0.0.1
*/
SELECTOR_IDENTIFIERS = createHashMap({
'#': 1,
'.': 2,
'[': 3,
':': 4
}),
PSEUDO_FIRST_CHILD = ':first-child',
PSEUDO_FIRST_OF_TYPE = ':first-of-type',
PSEUDO_LAST_CHILD = ':last-child',
PSEUDO_LAST_OF_TYPE = ':last-of-type',
PSEUDO_NTH_CHILD = ':nth-child',
PSEUDO_NTH_LAST_CHILD = ':nth-last-child',
PSEUDO_NTH_LAST_OF_TYPE = ':nth-last-of-type',
PSEUDO_NTH_OF_TYPE = ':nth-of-type',
PSEUDO_ONLY_OF_TYPE = ':only-of-type',
PSEUDO_ONLY_CHILD = ':only-child',
/**
* Object to gain quick access to the selectors that required children
*
* @property PSEUDO_REQUIRED_CHILDREN
* @default {
* ':first-child': true,
* ':first-of-type': true,
* ':last-child': true,
* ':last-of-type': true,
* ':nth-child': true,
* ':nth-last-child': true,
* ':nth-last-of-type': true,
* ':nth-of-type': true,
* ':only-of-type': true,
* ':only-child': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
PSEUDO_REQUIRED_CHILDREN = createHashMap(),
_matchesSelectorItem, _matchesOneSelector, _findElementSibling, vNodeProto, _markRemoved, _tryReplaceChild,
_splitSelector, _findNodeSibling, _matchNthChild, _batchEmit, _emitDestroyChildren, _tryRemoveDomNode;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_CHILD] = true;
/**
* Searches for the next -or previous- node-sibling (nodeType of 1, 3 or 8).
*
* @method _findNodeSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findNodeSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
index = vParent.vChildNodes.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildNodes[index];
};
/**
* Searches for the next -or previous- Element-sibling (nodeType of 1).
*
* @method _findElementSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findElementSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
if (vnode.nodeType===1) {
index = vParent.vChildren.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildren[index];
}
else {
/*jshint noempty:true */
while ((vnode=_findNodeSibling(vnode, next)) && (vnode.nodeType!==1)) {}
/*jshint noempty:false */
return vnode;
}
};
/**
* Check whether the vnode matches a "nth-child" test, which is used for css pseudoselectors like `nth-child`, `nth-of-type` etc.
*
* @method _matchNthChild
* @param pseudoArg {String} the argument for nth-child
* @param index {Number} the index of the inspected vnode
* @return {Boolean} whether the vnode matches the nthChild test
* @protected
* @private
* @since 0.0.1
*/
_matchNthChild = function(pseudoArg, index) {
var match, k, a, b, nodeOk, nthIndex, sign, isNumber;
(pseudoArg==='even') && (pseudoArg='2n');
(pseudoArg==='odd') && (pseudoArg='2n+1');
match = pseudoArg.match(NTH_CHILD_REGEXP) || (isNumber=pseudoArg.validateNumber());
if (!match) {
return false;
}
// pseudoArg follows the pattern: `an+b`
if (!isNumber) {
a = match[1];
sign = match[2];
b = match[3] || 0;
(b==='') && (b=0);
}
else {
b = pseudoArg;
}
sign && (sign=sign[0]);
if (!a) {
// only fixed index to match
return (sign==='-') ? false : (parseInt(b, 10)===index);
}
else {
// we need to iterate
nodeOk = false;
b = window.Number(b);
for (k=0; !nodeOk; k++) {
nthIndex = (sign==='-') ? (a*k) - b : (a*k) + b;
if (nthIndex===index) {
nodeOk = true;
}
else if (nthIndex>index) {
// beyond index --> will never become a fix anymore
return false;
}
}
return nodeOk;
}
};
/**
* Check whether the vnode matches the css-selector. the css-selector should be a single selector,
* not multiple, so it shouldn't contain a `comma`.
*
* @method _matchesOneSelector
* @param vnode {vnode} the vnode to inspect
* @param selector {String} the selector-item to check the match for
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches the css-selector
* @protected
* @private
* @since 0.0.1
*/
_matchesOneSelector = function(vnode, selector, relatedVNode) {
var selList = _splitSelector(selector),
size = selList.length,
selMatch = false,
i, selectorItem, last,
rightvnode, relationMatch, checkRelation;
if (STORABLE_SPLIT_CHARACTER[selList[size-1]]) {
return false;
}
relationMatch = function(leftVNode, rightVNode, relationCharacter) {
var match, vParent, vChildren;
// when `selector` starts with `>`, `~` or `+`, then
// there should also be a match comparing a related node!
switch (relationCharacter) {
case '>':
leftVNode || (leftVNode=rightVNode.vParent);
match = (leftVNode.vChildren.indexOf(rightVNode)!==-1);
break;
case '~':
leftVNode || (leftVNode=rightVNode.vFirstElement);
vParent = leftVNode.vParent;
vChildren = vParent && vParent.vChildren;
match = vParent && (vChildren.indexOf(leftVNode)<vChildren.indexOf(rightVNode));
break;
case '+':
leftVNode || (leftVNode=rightVNode.vPreviousElement);
match = (leftVNode.vNextElement === rightVNode);
}
return !!match;
};
last = size - 1;
i = last;
while (vnode && ((selMatch || (i===last)) && (i>=0))) {
selectorItem = selList[i];
if (STORABLE_SPLIT_CHARACTER[selectorItem]) {
checkRelation = selectorItem;
i--;
}
else {
selMatch = _matchesSelectorItem(vnode, selectorItem);
if (!selMatch && (i===last)) {
return false;
}
while (!selMatch && vnode && (i!==last)) {
// may also look at nodes higher in the chain
vnode = SIBLING_MATCH_CHARACTER[selList[i+1]] ? vnode.vPreviousElement : vnode.vParent;
selMatch = vnode && _matchesSelectorItem(vnode, selectorItem);
}
selMatch && checkRelation && vnode && (selMatch=relationMatch(vnode, rightvnode, checkRelation));
rightvnode = vnode;
if (vnode) {
// do a precheck on the next checkRelation:
// and determine whether to set the vnode upper the tree or at the previous sibling:
vnode = SIBLING_MATCH_CHARACTER[selList[i-1]] ? vnode.vPreviousElement : vnode.vParent;
}
if (selMatch) {
checkRelation = false;
i--;
}
}
}
if ((rightvnode===relatedVNode) || (i!==-1)) {
selMatch = false;
}
selMatch && checkRelation && rightvnode && (selMatch=relationMatch(relatedVNode, rightvnode, checkRelation));
return selMatch;
};
/**
* Check whether the vnode matches one specific selector-item. Suppose the css-selector: "#mynode li.red .blue"
* then there are 3 selector-items: "#mynode", "li.red" and ".blue"
*
* This method also can handle the new selectors:
* <ul>
* <li>[att^=val] –-> the “begins with” selector</li>
* <li>[att$=val] –-> the “ends with” selector</li>
* <li>[att*=val] –-> the “contains” selector (might be a substring)</li>
* <li>[att~=val] –-> the “contains” selector as a separate word, separated by spaces</li>
* <li>[att|=val] –-> the “contains” selector as a separate word, separated by `|`</li>
* <li>+ --> (same level)</li>
* <li>~ --> (same level)</li>
* </ul>
*
* @method _matchesSelectorItem
* @param vnode {Object} the vnode to inspect
* @param selectorItem {String} the selector-item to check the match for
* @return {Boolean} whether the vnode matches the selector-item
* @protected
* @private
* @since 0.0.1
*/
_matchesSelectorItem = function (vnode, selectorItem) {
var i = 0,
len = selectorItem.length,
character = selectorItem[0],
tagName, id, className, attributeName, attributeValue, stringMarker, attributeisString, isBoolean, insideAttributeValue, insideAttribute,
vParent, checkBoolean, treatment, k, min, max, value, len2, index, found, pseudo, pseudoArg, arglevel, count, vParentVChildren;
if (selectorItem==='*') {
return true;
}
if (!SELECTOR_IDENTIFIERS[character]) {
// starts with tagName
tagName = '';
// reposition i to continue in the right way:
i--;
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
tagName += character;
}
if (tagName.toUpperCase()!==vnode.tag) {
return false;
}
}
while (i<len) {
switch (character) {
case '#':
id = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
id += character;
}
if (id!==vnode.id) {
return false;
}
break;
case '.':
className = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
className += character;
}
if (!vnode.hasClass(className)) {
return false;
}
break;
case '[':
attributeName = '';
while ((++i<len) && (character=selectorItem[i]) && !END_ATTRIBUTENAME[character]) {
attributeName += character;
}
// if character===']' then we have an attribute without a value-definition
if (!vnode.attrs[attributeName] || ((character===']') && (vnode.attrs[attributeName]!==''))) {
return !!vnode.attrs[attributeName];
}
// now we read the value of the attribute
// however, it could be that the selector has a special `detailed` identifier set (defined by: ATTR_DETAIL_SPECIFIERS)
if (ATTR_DETAIL_SPECIFIERS[character]) {
treatment = character; // store the character to know how the attributedata should be treaded
i++; // character should be a "=" by now
}
else {
treatment = null;
}
attributeValue = '';
stringMarker = selectorItem[i+1];
attributeisString = (stringMarker==='"') || (stringMarker==="'");
attributeisString && (i++);
// end of attributaValue = (character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker))
while ((++i<len) && (character=selectorItem[i]) && !((character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker)))) {
attributeValue += character;
}
if (attributeisString) {
// if attribute is string, then we need to _remove to last stringmarker
attributeValue = attributeValue.substr(0, attributeValue.length-1);
}
else {
// if attribute is no string, then we need to typecast its value
isBoolean = ((attributeValue.length>3) && (attributeValue.length<6) &&
(checkBoolean=attributeValue.toUpperCase()) &&
((checkBoolean==='FALSE') || (checkBoolean==='TRUE')));
// typecast the value to either Boolean or Number:
attributeValue = isBoolean ? (checkBoolean==='TRUE') : parseFloat(attributeValue);
}
// depending upon how the attributedata should be treated:
if (treatment) {
switch (treatment) {
case '^': // “begins with” selector
if (!vnode.attrs[attributeName].startsWith(attributeValue)) {
return false;
}
break;
case '$': // “ends with” selector
if (!vnode.attrs[attributeName].endsWith(attributeValue)) {
return false;
}
break;
case '*': // “contains” selector (might be a substring)
if (!vnode.attrs[attributeName].contains(attributeValue)) {
return false;
}
break;
case '~': // “contains” selector as a separate word, separated by spaces
if (!(' '+vnode.attrs[attributeName]+' ').contains(' '+attributeValue+' ')) {
return false;
}
break;
case '|': // “contains” selector as a separate word, separated by `|`
if (!('|'+vnode.attrs[attributeName]+'|').contains('|'+attributeValue+'|')) {
return false;
}
break;
}
}
else if (vnode.attrs[attributeName]!==attributeValue) {
return false;
}
// we still need to increase one position:
(++i<len) && (character=selectorItem[i]);
break;
case ':':
// we have a pseudo-selector
// first, find out which one
// because '::' is a valid start (though without any selection), we start to back the next character as well:
pseudo = ':'+selectorItem[++i];
pseudoArg = '';
vParent = vnode.vParent;
vParentVChildren = vParent && vParent.vChildren;
// pseudo-selectors might have an argument passed in, like `:nth-child(2n+1)` or `:not([type="checkbox"])` --> we
// store this argument inside `pseudoArg`
// also note that combinations are possible with `:not` --> `:not(:nth-child(2n+1))`
// also note that we cannot "just" look for a closing character when running into the usage of attributes:
// for example --> `:not([data-x="some data :)"])`
// that's why -once we are inside attribute-data- we need to continue until the attribute-data ends
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
if (character==='(') {
// starting arguments
arglevel = 1;
insideAttribute = false;
insideAttributeValue = false;
while ((++i<len) && (character=selectorItem[i]) && (arglevel>0)) {
if (!insideAttribute) {
if (character==='(') {
arglevel++;
}
else if (character===')') {
arglevel--;
}
else if (character==='[') {
insideAttribute = true;
}
}
else {
// inside attribute
if (!insideAttributeValue) {
if ((character==='"') || (character==="'")) {
insideAttributeValue = true;
stringMarker = character;
}
else if (character===']') {
insideAttribute = false;
}
}
else if ((character===stringMarker) && (selectorItem[i+1]===']')) {
insideAttributeValue = false;
}
}
(arglevel>0) && (pseudoArg+=character);
}
}
else {
pseudo += character;
}
}
// now, `pseudo` is known as well as its possible pseudoArg
if (!vParentVChildren && PSEUDO_REQUIRED_CHILDREN[pseudo]) {
return false;
}
switch (pseudo) {
case ':checked': // input:checked Selects every checked <input> element
if (!vnode.attrs.checked) {
return false;
}
break;
case ':disabled': // input:disabled Selects every disabled <input> element
if (!vnode.attrs.disabled) {
return false;
}
break;
case ':empty': // p:empty Selects every <p> element that has no children (including text nodes)
if (vnode.vChildNodes && (vnode.vChildNodes.length>0)) {
return false;
}
break;
case ':enabled': // input:enabled Selects every enabled <input> element
if (vnode.attrs.disabled) {
return false;
}
break;
case PSEUDO_FIRST_CHILD: // p:first-child Selects every <p> element that is the first child of its parent
if (vParentVChildren[0]!==vnode) {
return false;
}
break;
case PSEUDO_FIRST_OF_TYPE: // p:first-of-type Selects every <p> element that is the first <p> element of its parent
for (k=vParentVChildren.indexOf(vnode)-1; k>=0; k--) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':focus': // input:focus Selects the input element which has focus
if (vnode.domNode!==DOCUMENT.activeElement) {
return false;
}
break;
case ':in-range': // input:in-range Selects input elements with a value within a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || (value<min) || (value>max)) {
return false;
}
break;
case ':lang': // p:lang(it) Selects every <p> element with a lang attribute equal to "it" (Italian)
if (vnode.attrs.lang!==pseudoArg) {
return false;
}
break;
case PSEUDO_LAST_CHILD: // p:last-child Selects every <p> element that is the last child of its parent
if (vParentVChildren[vParentVChildren.length-1]!==vnode) {
return false;
}
break;
case PSEUDO_LAST_OF_TYPE: // p:last-of-type Selects every <p> element that is the last <p> element of its parent
len2 = vParentVChildren.length;
for (k=vParentVChildren.indexOf(vnode)+1; k<len2; k++) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':not': // :not(p) Selects every element that is not a <p> element
if (vnode.matchesSelector(pseudoArg)) {
return false;
}
break;
case PSEUDO_NTH_CHILD: // p:nth-child(2) Selects every <p> element that is the second child of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
index = vParentVChildren.indexOf(vnode)+1;
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_CHILD: // p:nth-last-child(2) Selects every <p> element that is the second child of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_OF_TYPE: // p:nth-last-of-type(2) Selects every <p> element that is the second <p> element of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
index = 0;
for (k=vParentVChildren.length-1; (k>=0) && !found; k--) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_OF_TYPE: // p:nth-of-type(2) Selects every <p> element that is the second <p> element of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
len2 = vParentVChildren.length;
index = 0;
for (k=0; (k<len2) && !found; k++) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_ONLY_OF_TYPE: // p:only-of-type Selects every <p> element that is the only <p> element of its parent
len2 = vParentVChildren.length;
count = 0;
for (k=0; (k<len2) && (count<=1); k++) {
(vParentVChildren[k].tag===vnode.tag) && count++;
}
if (count!==1) {
return false;
}
break;
case PSEUDO_ONLY_CHILD: // p:only-child Selects every <p> element that is the only child of its parent
if (vParentVChildren.length!==1) {
return false;
}
break;
case ':optional': // input:optional Selects input elements with no "required" attribute
if (vnode.attrs.required) {
return false;
}
break;
case ':out-of-range': // input:out-of-range Selects input elements with a value outside a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || ((value>=min) && (value<=max))) {
return false;
}
break;
case ':read-only': // input:read-only Selects input elements with the "readonly" attribute specified
if (!vnode.attrs.readonly) {
return false;
}
break;
case ':read-write': // input:read-write Selects input elements with the "readonly" attribute NOT specified
if (vnode.attrs.readonly) {
return false;
}
break;
case ':required': // input:required Selects input elements with the "required" attribute specified
if (!vnode.attrs.required) {
return false;
}
break;
case ':root': // Selects the document's root element
if (vnode.domNode!==DOCUMENT.documentElement) {
return false;
}
break;
}
}
}
return true;
};
/**
* Splits the selector into separate subselector-items that should match different elements through the tree.
* Special characters '>' and '+' are added as separate items in the hash.
*
* @method _splitSelector
* @param selector {String} the selector-item to check the match for
* @return {Array} splitted selectors
* @protected
* @private
* @since 0.0.1
*/
_splitSelector = function(selector) {
var list = [],
len = selector.length,
sel = '',
i, character, insideDataAttr;
for (i=0; i<len; i++) {
character = selector[i];
if (character==='[') {
sel += character;
insideDataAttr = true;
}
else if (character===']') {
sel += character;
insideDataAttr = false;
}
else if (insideDataAttr || !SPLIT_CHARACTER[character]) {
sel += character;
}
else {
// unique selectoritem is found, add it to the list
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
// in case the last character was '>', '+' or '~', we need to add it as a separate item
STORABLE_SPLIT_CHARACTER[character] && (list[list.length]=character);
}
}
// add the last item
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
return list;
};
_batchEmit = function() {
// we will exactly define the 'UI:'-event --> Itags will have another emitterName
MUTATION_EVENTS.each(function (mutationEvents, vnode) {
var domNode = vnode.domNode;
if (mutationEvents[EV_REMOVED]) {
domNode.emit('UI:'+EV_REMOVED);
}
else if (mutationEvents[EV_INSERTED]) {
domNode.emit('UI:'+EV_INSERTED);
}
else {
// contentchange and attributechanges can go hand in hand
mutationEvents.each(function(value, evt) {
domNode.emit('UI:'+evt, (evt===EV_CONTENT_CHANGE) ? null : {changed: value});
});
}
});
MUTATION_EVENTS.clear();
BATCH_WILL_RUN = false;
};
_emitDestroyChildren = function(vnode) {
var children = vnode.vChildren,
len = children.length,
i, vChild;
for (i=0; i<len; i++) {
vChild = children[i];
vChild._emit(EV_REMOVED);
}
};
_markRemoved = function(vnode) {
var vChildNodes = vnode.vChildNodes,
len, i, vChildNode;
if (vnode.nodeType===1) {
Object.protectedProp(vnode, 'removedFromDOM', true);
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && _markRemoved(vChildNode);
}
}
}
};
/**
* A safe way to remove a dom-node, even if it is not present.
* Will not throw a JS error when the "to be removed" node isn't in the dom
*
* @method _tryRemoveDomNode
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param childDomNode {DOMNode} the node that needs to be removed
* @since 0.0.1
*/
_tryRemoveDomNode = function(parentDomNode, childDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && childDomNode.isItag && childDomNode.isItag()) {
DOCUMENT._itagList.remove(childDomNode);
}
try {
parentDomNode._removeChild(childDomNode);
}
catch(err) {}
};
/**
* A safe way to replace a dom-node, even if the "to be replaced"-node it is not present.
* Will not throw a JS error when the "to be replaced" node isn't in the dom. in that case, the new node will
* be appended
*
* @method _tryReplaceChild
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param newChildDomNode {DOMNode} the node that needs to be replaced
* @param oldChildDomNode {DOMNode} the node that needs to be inserted
* @since 0.0.1
*/
_tryReplaceChild = function(parentDomNode, newChildDomNode, oldChildDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && oldChildDomNode.isItag && oldChildDomNode.isItag()) {
DOCUMENT._itagList.remove(oldChildDomNode);
}
try {
parentDomNode._replaceChild(newChildDomNode, oldChildDomNode);
}
catch(err) {
// if the childDomNode isn;t there any more - for whatever reason - the we need to append the newChildNode
parentDomNode._appendChild(newChildDomNode);
}
};
vNodeProto = window._ITSAmodules.VNode = {
/**
* Check whether the vnode's domNode is equal, or contains the specified Element.
*
* @method contains
* @return {Boolean} whether the vnode's domNode is equal, or contains the specified Element.
* @since 0.0.1
*/
contains: function(otherVNode, noProtectedSearch) {
var instance = this;
if (otherVNode && (otherVNode.destroyed || (noProtectedSearch && otherVNode._systemNode))) {
return false;
}
while (otherVNode && (otherVNode!==instance)) {
otherVNode = otherVNode.vParent;
if (otherVNode && noProtectedSearch && (otherVNode._systemNode || (otherVNode.isItag && otherVNode.domNode.contentHidden))) {
return false;
}
}
return (otherVNode===instance);
},
/**
* Empties the vnode.
*
* Syncs with the dom.
*
* @method empty
* @param [full=false] {Boolean} whether system-nodes should be removed as well
* @chainable
* @since 0.0.1
*/
empty: function(full) {
delete this._scripts;
return this._setChildNodes([], null, full);
},
/**
* Returns the first child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method firstOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the first child-vnode or null when not present
* @since 0.0.1
*/
firstOfVChildren: function(cssSelector) {
var instance = this,
found, i, len, vChildren, element;
if (!cssSelector) {
return instance.vFirstElementChild;
}
vChildren = instance.vChildren;
len = vChildren.length;
for (i=0; !found && (i<len); i++) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
return found;
},
/**
* Gets the innerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* Only valid for nodetype=1 (HTMLElements)
*
* @method getHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String|undefined} the innerHTML without the elements specified, or `undefined` when not an HTMLElement
* @since 0.0.1
*/
getHTML: function(exclude, includeSystemNodes) {
var instance = this,
html, vChildNodes, len, i, vChildNode;
if (instance.nodeType===1) {
if (exclude) {
Array.isArray(exclude) || (exclude=[exclude]);
}
html = '';
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
switch (vChildNode.nodeType) {
case 1:
(exclude && exclude.contains(vChildNode.domNode)) || (!includeSystemNodes && vChildNode._systemNode) || (html+=vChildNode.getOuterHTML(exclude, includeSystemNodes));
break;
case 3:
html += vChildNode.text.replace(/</g, '<').replace(/>/g, '>');
break;
case 8:
html += '<!--' + vChildNode.text.replace(/</g, '<').replace(/>/g, '>') + '-->';
}
}
}
return html;
},
/**
* Gets the outerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* @method getOuterHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String} the outerHTML
* @since 0.0.1
*/
getOuterHTML: function(exclude, includeSystemNodes) {
var instance = this,
html,
attrs = instance.attrs;
if (instance.nodeType===1) {
if (instance.nodeType!==1) {
return instance.textContent;
}
html = '<' + instance.tag.toLowerCase();
attrs.each(function(value, key) {
html += ' '+key+'="'+value+'"';
});
instance.isVoid && (html += '/');
html += '>';
if (!instance.isVoid) {
html += ((!includeSystemNodes && instance.isItag) ? '' : instance.getHTML(exclude, includeSystemNodes)) + '</' + instance.tag.toLowerCase() + '>';
}
}
return html;
},
/**
* Checks whether the vnode has any vChildNodes (nodeType of 1, 3 or 8).
*
* @method hasVChildNodes
* @return {Boolean} whether the vnode has any vChildNodes.
* @since 0.0.1
*/
hasVChildNodes: function() {
return this.vChildNodes ? (this.vChildNodes.length>0) : false;
},
/**
* Checks whether the vnode has any vChildren (vChildNodes with nodeType of 1).
*
* @method hasVChildren
* @return {Boolean} whether the vnode has any vChildren.
* @since 0.0.1
*/
hasVChildren: function() {
return this.vChildNodes ? (this.vChildren.length>0) : false;
},
/**
* Checks whether the className is present on the vnode.
*
* @method hasClass
* @param className {String|Array} the className to check for. May be an Array of classNames, which all needs to be present.
* @return {Boolean} whether the className (or classNames) is present on the vnode
* @since 0.0.1
*/
hasClass: function(className) {
var instance = this,
check = function(cl) {
return !!instance.classNames[cl];
};
if (!instance.classNames) {
return false;
}
if (typeof className === STRING) {
return check(className);
}
else if (Array.isArray(className)) {
return className.every(check);
}
return false;
},
/**
* Returns the last child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method lastOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the last child-vnode or null when not present
* @since 0.0.1
*/
lastOfVChildren: function(cssSelector) {
var vChildren = this.vChildren,
found, i, element;
if (vChildren) {
if (!cssSelector) {
return this.vLastElementChild;
}
for (i=vChildren.length-1; !found && (i>=0); i--) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
}
return found;
},
/**
* Checks whether the vnode matches one of the specified selectors. `selectors` can be one, or multiple css-selectors,
* separated by a `comma`. For example: "#myid li.red blue" is one selector, "div.red, div.blue, div.green" are three selectors.
*
* @method matchesSelector
* @param selectors {String} one or more css-selectors
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches one of the selectors
* @since 0.0.1
*/
matchesSelector: function(selectors, relatedVNode) {
var instance = this;
if (instance.nodeType!==1) {
return false;
}
selectors = selectors.split(',');
// we can use Array.some, because there won't be many separated selectoritems,
// so the final invocation won't be delayed much compared to looping
return selectors.some(function(selector) {
return _matchesOneSelector(instance, selector, relatedVNode);
});
},
/**
* Reloads the DOM-attribute into the vnode.
*
* @method matchesSelector
* @param attributeName {String} the name of the attribute to be reloaded.
* @return {Node} the domNode that was reloaded.
* @since 0.0.1
*/
reloadAttr: function(attributeName) {
var instance = this,
domNode = instance.domNode,
attributeValue = domNode._getAttribute(attributeName),
attrs = instance.attrs,
extractStyle, extractClass;
if (instance.nodeType===1) {
attributeValue || (attributeValue='');
if (attributeValue==='') {
delete attrs[attributeName];
// in case of STYLE attributeName --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attributeName --> special treatment
(attributeName===CLASS) && (instance.classNames={});
// in case of ID attributeName --> special treatment
if ((attributeName===ID) && (instance.id)) {
delete nodeids[instance.id];
delete instance.id;
}
}
else {
attrs[attributeName] = attributeValue;
// in case of STYLE attributeName --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(attributeValue);
attributeValue = extractStyle.attrStyle;
if (attributeValue) {
attrs.style = attributeValue;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attributeName --> special treatment
extractClass = extractor.extractClass(attributeValue);
attributeValue = extractClass.attrClass;
if (attributeValue) {
attrs[CLASS] = attributeValue;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (instance.id!==attributeValue) && (delete nodeids[instance.id]);
instance.id = attributeValue;
nodeids[attributeValue] = domNode;
}
}
}
return domNode;
},
/**
* Returns the vnode's style in a serialized form: the way it appears in the dom.
*
* @method serializeStyles
* @return {String} vnode's style
* @since 0.0.1
*/
serializeStyles: function() {
return extractor.serializeStyles(this.styles);
},
/**
* Sets the vnode's and dom-nodes inner HTML.
*
* Syncs with the dom. Can be invoked multiple times without issues.
*
* @method setHTML
* @param content {String} the innerHTML
* @param [suppressItagRender] {Boolean} to suppress Itags from rendering
* @param [allowScripts=false] {Boolean} whether scripts are allowed --> these should be defined with `xscript` instead of `script`
* @chainable
* @since 0.0.1
*/
setHTML: function(content, suppressItagRender, allowScripts) {
var instance = this;
instance._setChildNodes(htmlToVNodes(content, vNodeProto, instance.ns, null, suppressItagRender, allowScripts), suppressItagRender);
return instance;
},
/**
* Syncs the vnode's nodeid (if available) inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom. Can be invoked multiple times without issues.
*
* @method storeId
* @chainable
* @since 0.0.1
*/
storeId: function() {
// store node/vnode inside WeakMap:
var instance = this;
instance.id ? (nodeids[instance.id]=instance.domNode) : (delete nodeids[instance.id]);
return instance;
},
//---- private ------------------------------------------------------------------
_addToTaglist: function() {
var instance = this,
itagList;
if (instance.isItag) {
itagList = DOCUMENT.getItags(); // also reads the dom if the list isn't build yet
instance._data || Object.protectedProp(instance, '_data', {});
if (!instance._data.ce_destroyed && !itagList.contains(instance.domNode)) {
itagList.push(instance.domNode);
}
}
},
/**
* Adds a vnode to the end of the list of vChildNodes.
*
* Syncs with the DOM.
*
* @method _appendChild
* @param VNode {vnode} vnode to append
* @private
* @return {Node} the Node that was appended
* @since 0.0.1
*/
_appendChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
size, noProccessScript, scriptContent;
if ((VNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (VNode.attrs && VNode.attrs.src) ? VNode.attrs.src : VNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
VNode._moveToParent(instance);
instance.domNode._appendChild(domNode);
if (VNode.nodeType===3) {
size = instance.vChildNodes.length;
instance._normalize();
// if the size changed, then the domNode was merged
(size===instance.vChildNodes.length) || (domNode=instance.vChildNodes[instance.vChildNodes.length-1].domNode);
}
if (VNode.nodeType===1) {
VNode._addToTaglist();
VNode._emit(EV_INSERTED);
}
return domNode;
}
},
/**
* Removes the vnode from its parent vChildNodes- and vChildren-list.
*
* Does NOT sync with the dom.
*
* @method _deleteFromParent
* @private
* @chainable
* @since 0.0.1
*/
_deleteFromParent: function() {
var instance = this,
vParent = instance.vParent;
if (vParent && vParent.vChildNodes) {
vParent.vChildNodes.remove(instance);
// force to recalculate the vChildren on a next call:
(instance.nodeType===1) && (vParent._vChildren=null);
}
return instance;
},
/**
* Cleans up (empties) vnode's `_data`
*
* @method _cleanData
* @private
* @chainable
* @since 0.0.1
*/
_cleanData: function() {
var instance = this,
data = instance._data;
data && data.each(
function(value, key) {
delete data[key];
}
);
return instance;
},
/**
* Cleans up (removes) duplicated `style` definitions.
*
* @method _cleanupStyle
* @private
* @chainable
* @since 0.0.1
*/
_cleanupStyle: function() {
var instance = this,
compare = [],
removal = [],
vChildren = instance.vChildren,
len = vChildren.length,
vChild, i, styleContent, vChildInner;
for (i=0; i<len; i++) {
vChild = vChildren[i];
if (vChild.tag==='STYLE') {
vChildInner = vChild.vChildNodes[0];
if (vChildInner) {
styleContent = vChildInner.text;
if (compare.contains(styleContent)) {
removal[removal.length] = vChild;
}
else {
compare[compare.length] = styleContent;
}
}
else {
// empty style-tag
removal[removal.length] = vChild;
}
}
}
removal.forEach(function(vnode) {
instance._removeChild(vnode);
});
},
/**
* Destroys the vnode and all its vnode-vChildNodes.
* Removes it from its vParent.vChildNodes list,
* also removes its definitions inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom.
*
* @method _destroy
* @private
* @chainable
* @since 0.0.1
*/
_destroy: function(silent) {
var instance = this,
vChildNodes = instance.vChildNodes,
len, i, vChildNode, vParent, treeNodes;
if (!instance.destroyed) {
if (!silent) {
// Because we don't want to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
later(function() {
instance._emit(EV_REMOVED, null, null, null, true);
}, 5);
}
Object.protectedProp(instance, 'destroyed', true);
// first: determine the dom-tree, which module `event-dom` needs to determine where the node was before it was destroyed:
treeNodes = [instance];
vParent = instance.vParent;
while (vParent) {
treeNodes[treeNodes.length] = vParent;
vParent = vParent.vParent;
}
// mark all its vChildNodes so we can see if the node is in the DOM
_markRemoved(instance);
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && instance.isItag) {
DOCUMENT._itagList.remove(instance.domNode);
}
instance._scripts && (instance._scripts.length=0);
// The definite cleanup needs to be done after a timeout:
// someone might need to handle the Element when removed (fe to cleanup specific things)
later(function() {
instance._cleanData();
if (instance.nodeType===1) {
// _destroy all its vChildNodes
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && vChildNode._destroy(true);
}
}
}
instance._vChildren = null;
// explicitely set instance.domNode._vnode and instance.domNode to null in order to prevent problems with the GC (we break the circular reference)
delete instance.domNode._vnode;
// if valid id, then _remove the DOMnodeRef from internal hash
instance.id && delete nodeids[instance.id];
}, silent ? 0 : DESTROY_DELAY);
instance._deleteFromParent();
// Do not make domNode `null` --> it could be used even when not in the dom
}
return instance;
},
_emit: function(evt, attribute, newValue, prevValue, destroyEvt) {
/**
* Emitted by every Element that gets inserted.
*
* @event nodeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets removed.
*
* @event noderemove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets its content changed (innerHTML/innerText).
*
* @event nodecontentchange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute inserted.
*
* @event attributeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* </ul>
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute removed.
*
* @event attributeremove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Strings of the attributeNames that are removed
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute changed.
*
* @event attributechange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* <li>prevValue</li>
* </ul>
* @since 0.1
*/
var instance = this,
silent, attrMutations, mutationEvents, mutation, vParent;
if (!DOCUMENT.hasMutationSubs || (instance.nodeType!==1)) {
return;
}
silent = !!DOCUMENT._suppressMutationEvents;
if (!silent && (destroyEvt || !instance.destroyed)) {
// Because we don't wannt to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
mutationEvents = MUTATION_EVENTS.get(instance) || {};
if (attribute) {
attrMutations = mutationEvents[evt] || [];
if (evt===EV_ATTRIBUTE_REMOVED) {
mutation = attribute;
}
else {
mutation = {
attribute: attribute
};
if ((evt===EV_ATTRIBUTE_INSERTED) || (evt===EV_ATTRIBUTE_CHANGED)) {
mutation.newValue = newValue;
}
if ((evt===EV_ATTRIBUTE_CHANGED) && prevValue) {
mutation.prevValue = prevValue;
}
}
attrMutations.push(mutation);
mutationEvents[evt] = attrMutations;
}
else {
mutationEvents[evt] = true;
}
MUTATION_EVENTS.set(instance, mutationEvents);
// now set all parent to have a nodecontentchange:
vParent = instance.vParent;
vParent && vParent._emit(EV_CONTENT_CHANGE);
// in case of removal we need to emit EV_REMOVED for all children right now
// for they will be actually removed silently after a delay of 1 minute
(evt===EV_REMOVED) && _emitDestroyChildren(instance);
if (!BATCH_WILL_RUN) {
BATCH_WILL_RUN = true;
async(function() {
_batchEmit();
});
}
}
return instance;
},
/**
* Inserts `newVNode` before `refVNode`.
*
* Syncs with the DOM.
*
* @method _insertBefore
* @param newVNode {vnode} vnode to insert
* @param refVNode {vnode} The vnode before which newVNode should be inserted.
* @private
* @return {Node} the Node being inserted (equals domNode)
* @since 0.0.1
*/
_insertBefore: function(newVNode, refVNode) {
var instance = this,
domNode = newVNode.domNode,
index = instance.vChildNodes.indexOf(refVNode),
noProccessScript, scriptContent;
if ((newVNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (newVNode.attrs && newVNode.attrs.src) ? newVNode.attrs.src : newVNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
if (index!==-1) {
newVNode._moveToParent(instance, index);
instance.domNode._insertBefore(domNode, refVNode.domNode);
(newVNode.nodeType===3) && instance._normalize();
if (newVNode.nodeType===1) {
newVNode._addToTaglist();
newVNode._emit(EV_INSERTED);
}
return domNode;
}
else {
console.warn('trying to insert before, but no ref-node found. Will append the new node');
return instance._appendChild(newVNode);
}
}
},
/**
* Moves the vnode from its current parent.vChildNodes list towards a new parent vnode at the specified position.
*
* Does NOT sync with the dom.
*
* @method _moveToParent
* @param parentVNode {vnode} the parent-vnode
* @param [index] {Number} the position of the child. When not specified, it will be appended.
* @private
* @chainable
* @since 0.0.1
*/
_moveToParent: function(parentVNode, index) {
var instance = this,
vParent = instance.vParent;
instance._deleteFromParent();
instance.vParent = parentVNode;
parentVNode.vChildNodes || (parentVNode.vChildNodes=[]);
(typeof index==='number') ? parentVNode.vChildNodes.insertAt(instance, index) : (parentVNode.vChildNodes[parentVNode.vChildNodes.length]=instance);
// force to recalculate the vChildren on a next call:
vParent && (instance.nodeType===1) && (vParent._vChildren = null);
// force to recalculate the vChildren on a next call:
parentVNode && (instance.nodeType===1) && (parentVNode._vChildren=null);
return instance;
},
/**
* Removes empty TextNodes and merges following TextNodes inside the vnode.
*
* Syncs with the dom.
*
* @method _normalize
* @private
* @chainable
* @since 0.0.1
*/
_normalize: function() {
var instance = this,
domNode = instance.domNode,
vChildNodes = instance.vChildNodes,
changed = false,
i, preChildNode, vChildNode;
if (!instance._unNormalizable && vChildNodes) {
for (i=vChildNodes.length-1; i>=0; i--) {
vChildNode = vChildNodes[i];
preChildNode = vChildNodes[i-1]; // i will get the value `-1` eventually, which leads into undefined preChildNode
if (vChildNode.nodeType===3) {
if (vChildNode.text==='') {
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
else if (preChildNode && preChildNode.nodeType===3) {
preChildNode.text += vChildNode.text;
preChildNode.domNode.nodeValue = unescapeEntities(preChildNode.text);
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
}
}
}
changed && instance._emit(EV_CONTENT_CHANGE);
return instance;
},
/**
* Makes the vnode `normalizable`. Could be set to `false` when batch-inserting nodes, while `normalizaing` manually at the end.
* Afterwards, you should always reset `normalizable` to true.
*
* @method _normalizable
* @param value {Boolean} whether the vnode should be normalisable.
* @private
* @chainable
* @since 0.0.1
*/
_normalizable: function(value) {
var instance = this;
value ? (delete instance._unNormalizable) : (instance._unNormalizable=true);
return instance;
},
/**
* Prevents MutationObserver from making the dom sync with the vnode.
* Should be used when manipulating the dom from within the vnode itself (to preventing looping)
*
* @method _noSync
* @chainable
* @private
* @since 0.0.1
*/
_noSync: function() {
var instance = this;
if (!instance._nosync) {
instance._nosync = true;
async(function() {
instance._nosync = false;
});
}
return instance;
},
/**
* Removes the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _removeAttr
* @param attributeName {String}
* @private
* @chainable
* @since 0.0.1
*/
_removeAttr: function(attributeName, suppressItagRender) {
var instance = this,
attributeNameSplitted, ns;
if (instance.isItag && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
console.warn('Not allowed to remove the attribute '+attributeName);
return instance;
}
if (instance.attrs[attributeName]!==undefined) {
delete instance.attrs[attributeName];
// in case of STYLE attribute --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attribute --> special treatment
(attributeName===CLASS) && (instance.classNames={});
if (attributeName===ID) {
delete nodeids[instance.id];
delete instance.id;
}
instance._emit(EV_ATTRIBUTE_REMOVED, attributeName);
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
instance.domNode._removeAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName);
}
else {
instance.domNode._removeAttribute(attributeName);
}
}
return instance;
},
/**
* Removes the vnode's child-vnode from its vChildren and the DOM.
*
* Syncs with the DOM.
*
* @method removeChild
* @param VNode {vnode} the child-vnode to remove
* @private
* @since 0.0.1
*/
_removeChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
hadFocus = domNode && domNode.hasFocus && domNode.hasFocus() && (VNode.attrs['fm-lastitem']==='true'),
parentVNode = VNode.vParent;
VNode._destroy();
_tryRemoveDomNode(instance.domNode, VNode.domNode);
instance._normalize();
// now, reset the focus on focusmanager when needed:
if (hadFocus) {
while (parentVNode && !parentVNode.attrs['fm-manage']) {
parentVNode = parentVNode.vParent;
}
parentVNode && parentVNode.domNode.focus();
}
},
/**
* Replaces the current vnode at the parent.vChildNode list by `newVNode`
*
* Does NOT sync with the dom.
*
* @method _replaceAtParent
* @param newVNode {Object} the new vnode which should take over the place of the current vnode
* @private
* @chainable
* @since 0.0.1
*/
_replaceAtParent: function(newVNode) {
var instance = this,
vParent = instance.vParent,
vChildNodes, index;
if (vParent && (vChildNodes=vParent.vChildNodes)) {
index = vChildNodes.indexOf(instance);
// force to recalculate the vChildren on a next call:
((instance.nodeType===1) || (newVNode.nodeType===1)) && (instance.vParent._vChildren=null);
vChildNodes[index] = newVNode;
}
return instance._destroy();
},
/**
* Sets the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _setAttr
* @param attributeName {String}
* @param value {String} the value for the attributeName
* @param [force=false] {Boolean} force the attribute to be set, even if restrictions would deny it
* @private
* @chainable
* @since 0.0.1
*/
_setAttr: function(attributeName, value, force, suppressItagRender) {
var instance = this,
extractStyle, extractClass,
attrs = instance.attrs,
prevVal = attrs[attributeName],
domNode = instance.domNode,
attributeNameSplitted, ns;
if (instance.isItag && !force && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
if (prevVal!=value) {
console.warn('Not allowed to set the attribute '+attributeName);
}
return instance;
}
// don't check by !== --> value isn't parsed into a String yet
if (prevVal && ((value===undefined) || (value===null))) {
instance._removeAttr(attributeName, suppressItagRender);
return instance;
}
// attribute-values are always Strings:
value = String(value);
// attribute-values will be stored without " or '
value = value.replace(/"/g, '"').replace(/'/g, "'");
if (prevVal!=value) {
attrs[attributeName] = value;
// in case of STYLE attribute --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(value);
value = extractStyle.attrStyle;
if (value) {
attrs.style = value;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attribute --> special treatment
extractClass = extractor.extractClass(value);
value = extractClass.attrClass;
if (value) {
attrs[CLASS] = value;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (delete nodeids[instance.id]);
instance.id = value;
nodeids[value] = domNode;
}
instance._emit(prevVal ? EV_ATTRIBUTE_CHANGED : EV_ATTRIBUTE_INSERTED, attributeName, value, prevVal);
// when set in the dom --> quotes need to be set as "
value = value.replace(/"/g, '"');
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
domNode._setAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName, value);
}
else {
domNode._setAttribute(attributeName, value);
}
}
return instance;
},
/**
* Redefines the attributes of both the vnode as well as its related dom-node. The new
* definition replaces any previous attributes (without touching unmodified attributes).
* the `is` attribute cannot be changed for itags.
*
* Syncs the new vnode's attributes with the dom.
*
* @method _setAttrs
* @param newAttrs {Object|Array} the new attributes to be set
* @private
* @chainable
* @since 0.0.1
*/
_setAttrs: function(newAttrs, suppressItagRender) {
// does sync the DOM
var instance = this,
attrsObj, attr, attrs, i, key, keys, len, value;
if (instance.nodeType!==1) {
return;
}
instance._noSync();
attrs = instance.attrs;
attrs.id && (delete nodeids[attrs.id]);
if (Object.isObject(newAttrs)) {
attrsObj = newAttrs;
}
else {
attrsObj = {};
len = newAttrs.length;
for (i=0; i<len; i++) {
attr = newAttrs[i];
attrsObj[attr.name] = attr.value;
}
}
if (instance.isItag && !suppressItagRender) {
if (attrs.is) {
attrsObj.is = attrs.is;
}
else {
delete attrsObj.is;
delete attrsObj.Is;
delete attrsObj.iS;
delete attrsObj.IS;
}
}
// first _remove the attributes that are no longer needed.
// quickest way for object iteration: http://jsperf.com/object-keys-iteration/20
keys = Object.keys(attrs);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
attrsObj[key] || instance._removeAttr(key, suppressItagRender);
}
// next: every attribute that differs: redefine
keys = Object.keys(attrsObj);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
value = attrsObj[key];
(attrs[key]===value) || instance._setAttr(key, value, suppressItagRender);
}
return instance;
},
/**
* Redefines the childNodes of both the vnode as well as its related dom-node. The new
* definition replaces any previous nodes. (without touching unmodified nodes).
*
* Syncs the new vnode's childNodes with the dom.
*
* @method _setChildNodes
* @param newVChildNodes {Array} array with vnodes which represent the new childNodes
* @private
* @chainable
* @since 0.0.1
*/
_setChildNodes: function(newVChildNodes, suppressItagRender, removeSystemElements) {
// does sync the DOM
var instance = this,
vChildNodes = instance.vChildNodes || [],
domNode = instance.domNode,
forRemoval = [],
i, oldChild, newChild, newLength, len, len2, childDomNode, nodeswitch, bkpAttrs, cleanupStyle,
process, bkpChildNodes, needNormalize, prevSuppress, scriptContent, _scripts, scriptLen, j;
instance._noSync();
// first: reset ._vChildren --> by making it empty, its getter will refresh its list on a next call
instance._vChildren = null;
// if newVChildNodes is undefined, then we assume it to be empty --> an empty array
newVChildNodes || (newVChildNodes=[]);
// quickest way to loop through array is by using for loops: http://jsperf.com/array-foreach-vs-for-loop/5
len = vChildNodes.length;
// we need to add the systemNodes -if any- to the new newVChildNodes: they should be retained.
// SystemNodes are always places at the beginning, so they won't get reshuffled:
if (!removeSystemElements) {
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
if (oldChild._systemNode) {
newVChildNodes.insertAt(oldChild, i);
}
else {
break;
}
}
}
newLength = newVChildNodes.length;
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
childDomNode = oldChild.domNode;
if (i < newLength) {
newChild = newVChildNodes[i];
newChild.vParent || (newChild.vParent=instance);
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
/*jshint boss:true */
switch (nodeswitch=NODESWITCH[oldChild.nodeType][newChild.nodeType]) {
/*jshint boss:false */
case 1: // oldNodeType==Element, newNodeType==Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
if ((oldChild.tag!==newChild.tag) ||
((oldChild.tag===newChild.tag) && oldChild.isItag && (oldChild.attrs.is!==newChild.attrs.is))) {
// new tag --> completely replace
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
/*jshint proto:true */
oldChild.isItag && oldChild.domNode.destroyUI && oldChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
newChild.attrs = {}; // reset to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
oldChild._replaceAtParent(newChild);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
else {
// same tag --> only update what is needed
// NOTE: when this._unchangableAttrs exists, an itag-element syncs its UI -->
if (oldChild._data) {
// we might need to set the class `itag-rendered` when the attributeData says so:
// this happens when an itag gets refreshed with an unrendered definition
if (oldChild._data.itagRendered && !newChild.hasClass('itag-rendered')) {
newChild.classNames['itag-rendered'] = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' '+'itag-rendered';
}
else {
newChild.attrs[CLASS] = 'itag-rendered';
}
}
// we might need to set the class `focussed` when the attributeData says so:
// this happens when an itag gets rerendered: its renderFn doesn't know if any elements
// were focussed
if (oldChild._data.focussed && !newChild.hasClass('focussed')) {
newChild.classNames.focussed = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' ' + 'focussed';
}
else {
newChild.attrs[CLASS] = 'focussed';
}
}
if (oldChild._data['fm-tabindex']) {
// node has the tabindex set by the focusmanager,
// but that info might got lost with re-rendering of the new element
newChild.attrs.tabindex = '0';
}
}
if (oldChild.isItag) {
prevSuppress = DOCUMENT._suppressMutationEvents || false;
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
/*jshint proto:true */
newChild.domNode.destroyUI && newChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
oldChild._setAttrs(newChild.attrs, suppressItagRender);
newChild._destroy(true); // destroy through the vnode and removing from DOCUMENT._itagList
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(prevSuppress);
}
else {
oldChild._setAttrs(newChild.attrs, suppressItagRender);
// next: sync the vChildNodes:
oldChild._setChildNodes(newChild.vChildNodes, suppressItagRender);
}
// reset ref. to the domNode, for it might have been changed by newChild:
oldChild.id && (nodeids[oldChild.id]=childDomNode);
newVChildNodes[i] = oldChild;
}
}
break;
case 2: // oldNodeType==Element, newNodeType==TextNode
// case2 and case3 should be treated the same
case 3: // oldNodeType==Element, newNodeType==Comment
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
oldChild._replaceAtParent(newChild);
instance._emit(EV_CONTENT_CHANGE);
break;
case 4: // oldNodeType==TextNode, newNodeType==Element
// case4 and case7 should be treated the same
case 7: // oldNodeType==Comment, newNodeType==Element
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
// oldChild.isVoid = newChild.isVoid;
// delete oldChild.text;
instance._emit(EV_CONTENT_CHANGE);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
break;
case 5: // oldNodeType==TextNode, newNodeType==TextNode
// case5 and case9 should be treated the same
case 9: // oldNodeType==Comment, newNodeType==Comment
if (oldChild.text!==newChild.text) {
oldChild.text = newChild.text;
oldChild.domNode.nodeValue = unescapeEntities(newChild.text);
instance._emit(EV_CONTENT_CHANGE);
}
newVChildNodes[i] = oldChild;
break;
case 6: // oldNodeType==TextNode, newNodeType==Comment
// case6 and case8 should be treated the same
case 8: // oldNodeType==Comment, newNodeType==TextNode
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = oldChild.vParent;
instance._emit(EV_CONTENT_CHANGE);
}
if ((nodeswitch===2) || (nodeswitch===5) || (nodeswitch===8)) {
needNormalize = true;
}
}
else {
// _remove previous definition
_tryRemoveDomNode(domNode, oldChild.domNode);
// the oldChild needs to be removed, however, this cannot be done right now, for it would effect the loop
// so we store it inside a hash to remove it later
forRemoval[forRemoval.length] = oldChild;
}
}
// now definitely remove marked childNodes:
len2 = forRemoval.length;
for (i=0; i<len2; i++) {
forRemoval[i]._destroy();
}
// now we add all new vChildNodes that go beyond `len`:
for (i = len; i < newLength; i++) {
newChild = newVChildNodes[i];
newChild.vParent = instance;
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
switch (newChild.nodeType) {
case 1: // Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
domNode._appendChild(newChild.domNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
break;
case 3: // TextNode
needNormalize = true;
// we need to break through --> no `break`
/* falls through */
default: // TextNode or CommentNode
// newChild.domNode.nodeValue = newChild.text;
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
domNode._appendChild(newChild.domNode);
instance._emit(EV_CONTENT_CHANGE);
}
newChild.storeId();
}
instance.vChildNodes = newVChildNodes;
needNormalize && instance._normalize();
cleanupStyle && instance._cleanupStyle();
return instance;
},
_setUnchangableAttrs: function(unchangableObj) {
this._unchangableAttrs = unchangableObj;
}
};
//---- properties ------------------------------------------------------------------
/**
* A hash of all the `attributes` of the vnode's representing dom-node.
*
* @property attrs
* @type Object
* @since 0.0.1
*/
/**
* Hash with all the classes of the vnode. Every class represents a key, all values are set `true`.
*
* @property classNames
* @type Object
* @since 0.0.1
*/
/**
* The `id` of the vnode's representing dom-node (if any).
*
* @property id
* @type String
* @since 0.0.1
*/
/**
* Tells whether tag is a void Element. Examples are: `br`, `img` and `input`. Non-void Elements are f.e. `div` and `table`.
* For TextNodes and CommentNodes, this property is `undefined`.
*
* @property isVoid
* @type Boolean
* @since 0.0.1
*/
/**
* The `nodeType` of the vnode's representing dom-node (1===ElementNode, 3===TextNode, 8===CommentNode).
*
* @property nodeType
* @type Number
* @since 0.0.1
*/
/**
* The `tag` of the vnode's representing dom-node (allways uppercase).
*
* @property tag
* @type String
* @since 0.0.1
*/
/**
* The `content` of the vnode's representing dom-node, in case it is a TextNode or CommentNode.
* Equals dom-node.nodeValue.
*
* Is `undefined` for ElementNodes.
*
* @property text
* @type String
* @since 0.0.1
*/
/**
* Hash with all the childNodes (vnodes). vChildNodes are any kind of vnodes (nodeType===1, 3 or 8)
*
* @property vChildNodes
* @type Array
* @since 0.0.1
*/
/**
* The underlying `dom-node` that the vnode represents.
*
* @property domNode
* @type domNode
* @since 0.0.1
*/
/**
* vnode's parentNode (defined as a vnode itself).
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
Object.defineProperties(vNodeProto, {
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property innerHTML
* @type String
* @since 0.0.1
*/
innerHTML: {
get: function() {
return this.getHTML();
},
set: function(v) {
this.setHTML(v);
}
},
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property nodeValue
* @type String
* @since 0.0.1
*/
nodeValue: {
get: function() {
var instance = this;
return ((instance.nodeType===3) || (instance.nodeType===8)) ? instance.text : null;
},
set: function(v) {
var instance = this,
newTextContent, prevTextContent;
if ((instance.nodeType===3) || (instance.nodeType===8)) {
prevTextContent = instance.domNode.textContent;
instance.domNode.textContent = v;
// set .text AFTER the dom-node is updated --> the content might be escaped!
newTextContent = instance.text = instance.domNode.textContent;
(newTextContent!==prevTextContent) && instance._emit(EV_CONTENT_CHANGE);
}
}
},
/**
* Gets or sets the outerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property outerHTML
* @type String
* @since 0.0.1
*/
outerHTML: {
get: function() {
return this.getOuterHTML();
},
set: function(v) {
var instance = this,
vParent = instance.vParent,
id = instance.attrs.id,
vnode, vnodes, bkpAttrs, bkpChildNodes, i, len, vChildNodes, isLastChildNode, index, refDomNode;
if ((instance.nodeType!==1) || !vParent) {
return;
}
instance._noSync();
vChildNodes = vParent.vChildNodes;
index = vChildNodes.indexOf(instance);
isLastChildNode = (index===(vChildNodes.length-1));
isLastChildNode || (refDomNode=vChildNodes[index+1].domNode);
vnodes = htmlToVNodes(v, vNodeProto, vParent.ns, vParent);
len = vnodes.length;
if (len>0) {
// the first vnode will replace the current instance:
vnode = vnodes[0];
if (vnode.nodeType===1) {
if (vnode.tag!==instance.tag) {
// new tag --> completely replace
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
id && (delete nodeids[id]);
vnode.attrs = {}; // reset to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
// vnode.attrs = bkpAttrs;
// vnode.vChildNodes = bkpChildNodes;
vnode.id && (nodeids[vnode.id]=vnode.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
else {
instance._setAttrs(vnode.attrs);
instance._setChildNodes(vnode.vChildNodes);
}
}
else {
id && (delete nodeids[id]);
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
}
for (i=1; i<len; i++) {
vnode = vnodes[i];
switch (vnode.nodeType) {
case 1: // Element
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
vnode.attrs = {}; // reset, to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._insertBefore(vnode.domNode, refDomNode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
break;
default: // TextNode or CommentNode
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._appendChild(vnode.domNode, refDomNode);
}
vnode.storeId();
vnode._moveToParent(vParent, index+i);
}
}
},
/**
* Gets or sets the innerContent of the Node as plain text.
*
* The setter syncs with the DOM.
*
* @property textContent
* @type String
* @since 0.0.1
*/
textContent: {
get: function() {
var instance = this,
text = '',
vChildNodes = instance.vChildNodes,
len, i, vChildNode;
if (instance.nodeType===1) {
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
text += (vChildNode.nodeType===3) ? vChildNode.text : ((vChildNode.nodeType===1) ? vChildNode.textContent : '');
}
}
else {
text = instance.text;
}
return text;
},
set: function(v) {
var vnode = Object.create(vNodeProto);
vnode.domNode = DOCUMENT.createTextNode(v);
// create circular reference:
vnode.domNode._vnode = vnode;
vnode.nodeType = 3;
vnode.text = vnode.domNode.textContent;
this._setChildNodes([vnode]);
}
},
/**
* Hash with all the children (vnodes). vChildren are vnodes that have a representing dom-node that is an HtmlElement (nodeType===1)
*
* @property vChildren
* @type Array
* @since 0.0.1
*/
vChildren: {
get: function() {
var instance = this,
children = instance._vChildren,
vChildNode, vChildNodes, i, len;
vChildNodes = instance.vChildNodes;
if (vChildNodes && !children) {
children = instance._vChildren = [];
len = vChildNodes.length;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
(vChildNode.nodeType===1) && (children[children.length]=vChildNode);
}
}
children || (children = instance._vChildren = []);
return children;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirst
* @type vnode
* @since 0.0.1
*/
vFirst: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstChild;
}
},
/**
* Reference to the first vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirstChild
* @type vnode
* @since 0.0.1
*/
vFirstChild: {
get: function() {
return (this.vChildNodes && this.vChildNodes[0]) || null;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vFirstElement
* @type vnode
* @since 0.0.1
*/
vFirstElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstElementChild;
}
},
/**
* Reference to the first vChild, where the related dom-node an Element (nodeType===1).
*
* @property vFirstElementChild
* @type vnode
* @since 0.0.1
*/
vFirstElementChild: {
get: function() {
return this.vChildren[0] || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLast
* @type vnode
* @since 0.0.1
*/
vLast: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastChild;
}
},
/**
* Reference to the last vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLastChild
* @type vnode
* @since 0.0.1
*/
vLastChild: {
get: function() {
var vChildNodes = this.vChildNodes;
return (vChildNodes && vChildNodes[vChildNodes.length-1]) || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vLastElement
* @type vnode
* @since 0.0.1
*/
vLastElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastElementChild;
}
},
/**
* Reference to the last vChild, where the related dom-node an Element (nodeType===1).
*
* @property vLastElementChild
* @type vnode
* @since 0.0.1
*/
vLastElementChild: {
get: function() {
var vChildren = this.vChildren;
return vChildren[vChildren.length-1] || null;
}
},
/**
* the Parent vnode
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
/**
* Reference to the next of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vNext
* @type vnode
* @since 0.0.1
*/
vNext: {
get: function() {
return _findNodeSibling(this, true);
}
},
/**
* Reference to the next of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vNextElement
* @type vnode
* @since 0.0.1
*/
vNextElement: {
get: function() {
return _findElementSibling(this, true);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vPrevious
* @type vnode
* @since 0.0.1
*/
vPrevious: {
get: function() {
return _findNodeSibling(this);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vPreviousElement
* @type vnode
* @since 0.0.1
*/
vPreviousElement: {
get: function() {
return _findElementSibling(this);
}
}
});
return vNodeProto;
};
},{"./attribute-extractor.js":2749,"./html-parser.js":2753,"./vdom-ns.js":2755,"js-ext/extra/hashmap.js":2707,"js-ext/extra/lightmap.js":2708,"js-ext/lib/array.js":2709,"js-ext/lib/object.js":2710,"js-ext/lib/string.js":2712,"polyfill":2729,"utils/lib/timers.js":2732}],2757:[function(require,module,exports){
arguments[4][100][0].apply(exports,arguments)
},{"./partials/extend-document.js":2751,"./partials/extend-element.js":2752,"./partials/node-parser.js":2754,"js-ext/extra/hashmap.js":2707,"js-ext/lib/object.js":2710,"utils/lib/timers.js":2732}],2758:[function(require,module,exports){
arguments[4][634][0].apply(exports,arguments)
},{"./lib/hammer-2.0.4.js":2759,"event-dom":2760}],2759:[function(require,module,exports){
module.exports=require(635)
},{"utils":2855}],2760:[function(require,module,exports){
"use strict";
/**
* Integrates DOM-events to event. more about DOM-events:
* http://www.smashingmagazine.com/2013/11/12/an-introduction-to-dom-events/
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @example
* Event = require('event-dom')(window);
*
* @module event
* @submodule event-dom
* @class Event
* @since 0.0.1
*/
var NAME = '[event-dom]: ',
Event = require('event'),
later = require('utils').later,
createHashMap = require('js-ext/extra/hashmap.js').createMap,
OUTSIDE = 'outside',
REGEXP_NODE_ID = /^#\S+$/,
REGEXP_EXTRACT_NODE_ID = /#(\S+)/,
REGEXP_UI_OUTSIDE = /^.+outside$/,
TIME_BTN_PRESSED = 200,
PURE_BUTTON_ACTIVE = 'pure-button-active',
UI = 'UI:',
NODE = 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
TAP = 'tap',
CLICK = 'click',
RIGHTCLICK = 'right'+CLICK,
CENTERCLICK = 'center'+CLICK,
EV_REMOVED = UI+NODE+REMOVE,
EV_INSERTED = UI+NODE+INSERT,
EV_CONTENT_CHANGE = UI+NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = UI+ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = UI+ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = UI+ATTRIBUTE+INSERT,
mutationEventsDefined = false,
NO_DEEP_SEARCH = {},
/*
* Internal hash containing all DOM-events that are listened for (at `document`).
*
* DOMEvents = {
* 'tap': callbackFn,
* 'mousemove': callbackFn,
* 'keypress': callbackFn
* }
*
* @property DOMEvents
* @default {}
* @type Object
* @private
* @since 0.0.1
*/
DOMEvents = {};
require('js-ext/lib/string.js');
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('polyfill/polyfill-base.js');
module.exports = function (window) {
var DOCUMENT = window.document,
_domSelToFunc, _evCallback, _findCurrentTargets, _preProcessor, _setupEvents, _setupMutationListener, _teardownMutationListener,
_setupDomListener, _teardownDomListener, SORT, _sortFunc, _sortFuncReversed, _getSubscribers, _selToFunc, MUTATION_EVENTS, preventClick;
require('vdom')(window);
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.EventDom) {
return Event; // Event was already extended
}
console.warn('event-dom 10');
MUTATION_EVENTS = [EV_REMOVED, EV_INSERTED, EV_CONTENT_CHANGE, EV_ATTRIBUTE_REMOVED, , EV_ATTRIBUTE_CHANGED, EV_ATTRIBUTE_INSERTED];
/*
* Transforms the selector to a valid function
*
* @method _selToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_selToFunc = function(customEvent, subscriber) {
Event._sellist.some(function(selFn) {
return selFn(customEvent, subscriber);
});
};
/*
* Creates a filterfunction out of a css-selector. To be used for catching any dom-element, without restrictions
* of any context (like Parcels can --> Parcel.Event uses _parcelSelToDom instead)
* On "non-outside" events, subscriber.t is set to the node that first matches the selector
* so it can be used to set as e.target in the final subscriber
*
* @method _domSelToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_domSelToFunc = function(customEvent, subscriber) {
// this stage is runned during subscription
var outsideEvent = REGEXP_UI_OUTSIDE.test(customEvent),
selector = subscriber.f,
context = subscriber.o,
vnode = subscriber.o.vnode,
isCustomElement = vnode && vnode.isItag,
visibleContent = isCustomElement && !vnode.domNode.contentHidden,
nodeid, byExactId, newTarget, deepSearch;
console.log(NAME, '_domSelToFunc type of selector = '+typeof selector);
// note: selector could still be a function: in case another subscriber
// already changed it.
if ((!selector && !isCustomElement) || (typeof selector === 'function')) {
subscriber.n || (subscriber.n = isCustomElement ? context : DOCUMENT);
return true;
}
selector || (selector='');
nodeid = selector.match(REGEXP_EXTRACT_NODE_ID);
nodeid ? (subscriber.nId=nodeid[1]) : (subscriber.n=isCustomElement ? context : DOCUMENT);
byExactId = REGEXP_NODE_ID.test(selector);
deepSearch = !NO_DEEP_SEARCH[customEvent];
// set the selector to `subscriber._s` so that e.currentTarget can calculate it:
subscriber._s = selector;
subscriber.f = function(e) {
// this stage is runned when the event happens
console.log(NAME, '_domSelToFunc inside filter. selector: '+selector);
var node = e.target,
vnode = node.vnode,
character1 = selector && selector.substr(1),
match = false;
if (!isCustomElement || visibleContent || context.contains(node)) {
if (selector==='') {
match = true;
}
else {
// e.target is the most deeply node in the dom-tree that caught the event
// our listener uses `selector` which might be a node higher up the tree.
// we will reset e.target to this node (if there is a match)
// note that e.currentTarget will always be `document` --> we're not interested in that
// also, we don't check for `node`, but for node.matchesSelector: the highest level `document`
// is not null, yet it doesn't have .matchesSelector so it would fail
// we DON'T want this for the focus and blur event!
if (deepSearch) {
if (vnode) {
// we go through the vdom
if (!vnode.removedFromDOM) {
while (vnode && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = vnode.domNode;
}
vnode = vnode.vParent;
}
}
}
else {
// we go through the dom
while (node.matchesSelector && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the dom');
match = byExactId ? (node.id===character1) : node.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = node;
}
node = node.parentNode;
}
}
}
else {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
if (vnode) {
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
}
else {
match = node.matchesSelector && (byExactId ? (node.id===character1) : node.matchesSelector(selector));
}
match && (e.sourceTarget=node);
}
}
}
if (outsideEvent && !match) {
// there is a match for the outside-event:
// we need to set e.sourceTarget and e.target:
newTarget = DOCUMENT.getElement(selector, true);
if (newTarget) {
e.sourceTarget = node;
subscriber.t = newTarget;
}
else {
// make return `false` because the selector is not in the dom
match = true;
}
}
console.log(NAME, '_domSelToFunc filter returns '+(!outsideEvent ? match : !match));
return !outsideEvent ? match : !match;
};
return true;
};
// at this point, we need to find out what are the current node-refs. whenever there is
// a filter that starts with `#` --> in those cases we have a bubble-chain, because the selector isn't
// set up with `document` at its root.
// we couldn't do this at time of subscribtion, for the nodes might not be there at that time.
// however, we only need to do this once: we store the value if we find them
// no problem when the nodes leave the dom later: the previous filter wouldn't pass
_findCurrentTargets = function(subscribers) {
console.log(NAME, '_findCurrentTargets');
subscribers.forEach(
function(subscriber) {
console.log(NAME, '_findCurrentTargets for single subscriber. nId: '+subscriber.nId);
subscriber.nId && (subscriber.n=DOCUMENT.getElementById(subscriber.nId, true));
}
);
};
/*
* Generates an event through our Event-system. Does the actual transportation from DOM-events
* into our Eventsystem. It also looks at the response of our Eventsystem: if our system
* halts or preventDefaults the customEvent, then the original DOM-event will be preventDefaulted.
*
* @method _evCallback
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_evCallback = function(e) {
console.log(NAME, '_evCallback');
var allSubscribers = Event._subs,
eType = e.type,
eventobject, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs, subsOutside,
subscribers, eventobjectOutside, wildcard_named_subsOutside, customEvent, eventName, which;
eventName = eType;
// first: a `click` event might be needed to transformed into `rightclick`:
if (eventName===CLICK) {
which = e.which;
(which===2) && (eventName=CENTERCLICK);
(which===3) && (eventName=RIGHTCLICK);
}
if (eventName==='tap') {
// prevent the next click-event
preventClick = true;
e.clientX || (e.clientX = e.center && e.center.x);
e.clientY || (e.clientY = e.center && e.center.y);
}
else if (preventClick && (eventName===CLICK)) {
preventClick = false;
return;
}
if (eventName===CLICK) {
eventName = 'tap';
e.center = {
x: e.clientX,
y: e.clientY
};
e.eventType = 4;
e.pointerType = 'mouse';
e.tapCount = 1;
}
customEvent = 'UI:'+eventName;
subs = allSubscribers[customEvent];
wildcard_named_subs = allSubscribers['*:'+eventName];
named_wildcard_subs = allSubscribers['UI:*'];
wildcard_wildcard_subs = allSubscribers['*:*'];
// Emit the dom-event though our eventsystem:
// NOTE: emit() needs to be synchronous! otherwise we wouldn't be able
// to preventDefault in time
//
// e = eventobject from the DOM-event OR gesture-event
// eventobject = eventobject from our Eventsystem, which get returned by calling `emit()`
// now so the work:
subscribers = _getSubscribers(e, true, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
eventobject = Event._emit(e.target, customEvent, e, subscribers, [], _preProcessor, false, true);
// now check outside subscribers
subsOutside = allSubscribers[customEvent+OUTSIDE];
wildcard_named_subsOutside = allSubscribers['*:'+eventName+OUTSIDE];
subscribers = _getSubscribers(e, true, subsOutside, wildcard_named_subsOutside);
eventobjectOutside = Event._emit(e.target, customEvent+OUTSIDE, e, subscribers, [], _preProcessor, false, true);
// if eventobject was preventdefaulted or halted: take appropriate action on
// the original dom-event. Note: only the original event can caused this, not the outsideevent
// stopPropagation on the original eventobject has no impact on our eventsystem, but who know who else is watching...
// be carefull though: not all gesture events have e.stopPropagation
eventobject.status.halted && e.stopPropagation && e.stopPropagation();
// now we might need to preventDefault the original event.
// be carefull though: not all gesture events have e.preventDefault
if ((eventobject.status.halted || eventobject.status.defaultPrevented || eventobject.status.defaultPreventedContinue) && e.preventDefault) {
e.preventDefault();
}
if (eventobject.status.ok) {
// last step: invoke the aftersubscribers
// we need to do this asynchronous: this way we pass them AFTER the DOM-event's defaultFn
// also make sure to paas-in the payload of the manipulated eventobject
subscribers = _getSubscribers(e, false, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent, eventobject, [], subscribers, _preProcessor, true), 10);
// now check outside subscribers
subscribers = _getSubscribers(e, false, subsOutside, wildcard_named_subsOutside);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent+OUTSIDE, eventobjectOutside, [], subscribers, _preProcessor, true), 10);
}
};
/*
* Creates an array of subscribers in the right order, conform their position in the DOM.
* Only subscribers that match the filter are involved.
*
* @method _getSubscribers
* @param e {Object} eventobject
* @param before {Boolean} whether it is a before- or after-subscriber
* @param subs {Array} array with subscribers
* @param wildcard_named_subs {Array} array with subscribers
* @param named_wildcard_subs {Array} array with subscribers
* @param wildcard_wildcard_subs {Array} array with subscribers
* @private
* @since 0.0.1
*/
_getSubscribers = function(e, before, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs) {
var subscribers = [],
beforeOrAfter = before ? 'b' : 'a',
saveConcat = function(extrasubs) {
extrasubs && extrasubs[beforeOrAfter] && (subscribers=subscribers.concat(extrasubs[beforeOrAfter]));
};
saveConcat(subs);
saveConcat(wildcard_named_subs);
saveConcat(named_wildcard_subs);
saveConcat(wildcard_wildcard_subs);
if (subscribers.length>0) {
subscribers = function(array, testFunc) {
// quickest way to filter an array: see http://jsperf.com/array-filter-performance/4
var filtered = array.slice(0), i;
for (i=array.length-1; i>=0; i--) {
console.log(NAME, 'filtercheck for subscriber');
testFunc(array[i]) || filtered.splice(i, 1);
}
return filtered;
}(subscribers, function(subscriber) {return (!subscriber.f || subscriber.f.call(subscriber.o, e));});
if (subscribers.length>0) {
// _findCurrentTargets(subscribers);
// sorting, based upon the sortFn
subscribers.sort(SORT);
}
}
return subscribers;
};
/*
* Sets e.target and e.sourceTarget for the single subscriber.
* Needs to be done for evenry single subscriber, because with a single event, these values change for each subscriber
*
* @method _preProcessor
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_preProcessor = function(subscriber, e) {
console.log(NAME, '_preProcessor');
// inside the aftersubscribers, we may need exit right away.
// this would be the case whenever stopPropagation or stopImmediatePropagation was called
// in case the subscribernode equals the node on which stopImmediatePropagation was called: return true
var propagationStopped, immediatePropagationStopped,
targetnode = (subscriber.t || subscriber.n);
immediatePropagationStopped = e.status.immediatePropagationStopped;
if (immediatePropagationStopped && ((immediatePropagationStopped===targetnode) || !immediatePropagationStopped.contains(targetnode))) {
console.log(NAME, '_preProcessor will return true because of immediatePropagationStopped');
return true;
}
// in case the subscribernode does not fall within or equals the node on which stopPropagation was called: return true
propagationStopped = e.status.propagationStopped;
if (propagationStopped && (propagationStopped!==targetnode) && !propagationStopped.contains(targetnode)) {
console.log(NAME, '_preProcessor will return true because of propagationStopped');
return true;
}
e._s = subscriber._s;
// now we might need to set e.target to the right node:
// the filterfunction might have found the true domnode that should act as e.target
// and set it at subscriber.t
// also, we need to backup the original e.target: this one should be reset when
// we encounter a subscriber with its own filterfunction instead of selector
if (subscriber.t) {
e.sourceTarget || (e.sourceTarget=e.target);
e.target = subscriber.t;
}
else {
e.sourceTarget && (e.target=e.sourceTarget);
}
return false;
};
/*
* Transports DOM-events to the Event-system. Catches events at their most early stage:
* their capture-phase. When these events happen, a new customEvent is generated by our own
* Eventsystem, by calling _evCallback(). This way we keep DOM-events and our Eventsystem completely separated.
*
* @method _setupDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_setupDomListener = function(customEvent, subscriber) {
console.log(NAME, '_setupDomListener');
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0],
eventName = eventSplitted[1],
outsideEvent = REGEXP_UI_OUTSIDE.test(eventName);
// be careful: anyone could also register an `outside`-event.
// in those cases, the DOM-listener must be set up without `outside`
outsideEvent && (eventName=eventName.substring(0, eventName.length-7));
// if eventName equals `mouseover` or `mouseleave` then we quit:
// people should use `mouseover` and `mouseout`
if ((eventName==='mouseenter') || (eventName==='mouseleave')) {
console.warn(NAME, 'Subscription to '+eventName+' not supported, use mouseover and mouseout: this eventsystem uses these non-noisy so they act as mouseenter and mouseleave');
return;
}
// only accept tap-events, yet later on we WILL need to listen for click-events
(eventName===CLICK) && (eventName=TAP);
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(emitterName+':'+eventName+(outsideEvent ? OUTSIDE : ''), subscriber);
// already registered? then return, also return if someone registered for UI:*
if (DOMEvents[eventName] || (eventName==='*')) {
// cautious: one might have registered the event, but not yet the outsideevent.
// in that case: save this setting:
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
return;
}
DOMEvents[eventName] = true;
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
// one exception: windowresize should listen to the window-object
if (eventName==='resize') {
window.addEventListener(eventName, _evCallback);
}
else {
((eventName===RIGHTCLICK) || (eventName===CENTERCLICK)) && (eventName=TAP);
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.addEventListener(eventName, _evCallback, true);
// listen for both `tap` and `click` events to happen
(eventName===TAP) && DOCUMENT.addEventListener(CLICK, _evCallback, true);
}
};
_setupEvents = function() {
var lastFocussed;
// make sure disabled buttons don't work:
Event.before(['tap', 'press'], function(e) {
e.preventDefault();
}, '.pure-button-disabled, button[disabled]');
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.before(
'keydown',
function(e) {
e._buttonPressed = true;
Event.emit(e.target, 'UI:tap', e);
},
function(e) {
var keyCode = e.keyCode;
return (e.target.getTagName()==='BUTTON') && ((keyCode===13) || (keyCode===32));
}
);
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.after(
'tap',
function(e) {
var buttonNode = e.target;
if (e._buttonPressed) {
buttonNode.setClass(PURE_BUTTON_ACTIVE);
e._noRender = true;
// even if the node isn't in the DOM, we can still try to manipulate it:
// the vdom makes sure no errors occur when the node is already removed
later(buttonNode.removeClass.bind(buttonNode, PURE_BUTTON_ACTIVE), TIME_BTN_PRESSED);
}
}
);
// fix activeElement on Mac
Event.before('focus', function(e) {
// will come here more often because of bubblechain
// however, the last pass-through will set the deepest node
lastFocussed = e.target;
});
// patching DOCUMENT.activeElement because it doesn't work well in a Mac: https://developer.mozilla.org/en-US/docs/Web/API/document.activeElement
// DOCUMENT._activeElement is used with the patch for DOCUMENT.activeElement its getter
Event.after('focus', function() {
DOCUMENT._activeElement = lastFocussed;
});
Event.before('blur', function() {
DOCUMENT._activeElement = null;
});
// Note: window.document has no prototype
Object.defineProperty(DOCUMENT, 'activeElement', {
get: function() {
return DOCUMENT._activeElement || DOCUMENT.body;
}
});
};
_setupMutationListener = function() {
DOCUMENT.hasMutationSubs = true;
if (!mutationEventsDefined) {
Event.defineEvent(EV_REMOVED).unPreventable();
Event.defineEvent(EV_INSERTED).unPreventable();
Event.defineEvent(EV_CONTENT_CHANGE).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_REMOVED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_CHANGED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_INSERTED).unPreventable();
mutationEventsDefined = true;
}
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFunc = function(subscriberOne, subscriberTwo) {
return (subscriberTwo.t || subscriberTwo.n).contains(subscriberOne.t || subscriberOne.n) ? -1 : 1;
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFuncReversed = function(subscriberOne, subscriberTwo) {
return (subscriberOne.t || subscriberOne.n).contains(subscriberTwo.t || subscriberTwo.n) ? 1 : -1;
};
/*
* Removes DOM-eventsubscribers from document when they are no longer needed.
*
* @method _teardownDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @private
* @since 0.0.2
*/
_teardownDomListener = function(customEvent) {
var customEventWithoutOutside = customEvent.endsWith(OUTSIDE) ? customEvent.substr(0, customEvent.length-7) : customEvent,
eventSplitted = customEventWithoutOutside.split(':'),
eventName = eventSplitted[1],
stillInUse;
if ((customEventWithoutOutside===CLICK) || (customEventWithoutOutside===RIGHTCLICK) || (customEventWithoutOutside===CENTERCLICK)) {
stillInUse = Event._subs[CLICK] ||
Event._subs[CLICK+OUTSIDE];
Event._subs[RIGHTCLICK] ||
Event._subs[RIGHTCLICK+OUTSIDE],
Event._subs[CENTERCLICK] ||
Event._subs[CENTERCLICK+OUTSIDE];
eventName = CLICK;
}
else {
stillInUse = Event._subs[customEventWithoutOutside] || Event._subs[customEventWithoutOutside+OUTSIDE];
}
if (!stillInUse) {
console.log(NAME, '_teardownDomListener '+customEvent);
// remove eventlistener from `document`
// one exeption: windowresize should listen to the window-object
if (eventName==='resize') {
window.removeEventListener(eventName, _evCallback);
}
else {
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.removeEventListener(eventName, _evCallback, true);
}
delete DOMEvents[eventName];
}
};
_teardownMutationListener = function() {
if (!Event._subs[EV_REMOVED] &&
!Event._subs[EV_INSERTED] &&
!Event._subs[EV_CONTENT_CHANGE] &&
!Event._subs[EV_ATTRIBUTE_REMOVED] &&
!Event._subs[EV_ATTRIBUTE_CHANGED] &&
!Event._subs[EV_ATTRIBUTE_INSERTED]
) {
DOCUMENT.hasMutationSubs = false;
}
};
// Now a very tricky one:
// Some browsers do an array.sort down-top instead of top-down.
// In those cases we need another sortFn, for the position on an equal match should fall
// behind instead of before (which is the case on top-down sort)
[1,2].sort(function(a /*, b */) {
SORT || (SORT=(a===2) ? _sortFuncReversed : _sortFunc);
});
// Now we do some initialization in order to make DOM-events work:
Object.defineProperty(Event._defaultEventObj, 'currentTarget', {
get: function() {
var ev = this,
e_target = ev.target;
if (e_target && ev._s) {
if (e_target.matches(ev._s)) {
return e_target;
}
return e_target.inside(ev._s) || undefined;
}
}
});
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(UI+'*', _setupDomListener, Event)
._setEventObjProperty('stopPropagation', function() {this.status.ok || (this.status.propagationStopped = this.target);})
._setEventObjProperty('stopImmediatePropagation', function() {this.status.ok || (this.status.immediatePropagationStopped = this.target);});
// Notify when someone subscribes to any event at all --> we might need to transform the filterFn from a selector into a true fnction
// this is already done automaticly by _setupDomListener fo UI:* events
Event.notify('*:*', function(customEvent, subscriber) {
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0];
if ((emitterName!=='UI') && (typeof subscriber.f==='string')) {
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(customEvent, subscriber);
}
}, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(UI+'*', _teardownDomListener, Event);
Event._sellist = [_domSelToFunc];
_setupEvents();
// making HTMLElement to be able to emit using event-emitter:
(function(HTMLElementPrototype) {
HTMLElementPrototype.merge(Event.Emitter('UI'));
}(window.HTMLElement.prototype));
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(MUTATION_EVENTS, _setupMutationListener, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(MUTATION_EVENTS, _teardownMutationListener, Event);
// Note: window.document has no prototype
DOCUMENT.suppressMutationEvents = function(suppress) {
this._suppressMutationEvents = suppress;
};
// Event.noDeepDomEvt and Event._domCallback are the only method that is added to Event.
// We need to do this, because `event-mobile` needs access to the same method.
// We could have done without this method and instead listen for a custom-event to handle
// Mobile events, however, that would lead into 2 eventcycli which isn't performant.
/**
*
* @method noDeepDomEvt
* @param domEvent {String} the eventName that should be processed without deepsearch
* @param e {Object} eventobject
* @for Event
* @chainable
* @since 0.0.1
*/
Event.noDeepDomEvt = function(domEvent) {
domEvent.contains(':') || (domEvent=UI+domEvent);
NO_DEEP_SEARCH[domEvent] = true;
return this;
};
/**
* Does the actual transportation from DOM-events into the Eventsystem. It also looks at the response of
* the Eventsystem: on e.halt() or e.preventDefault(), the original DOM-event will be preventDefaulted.
*
* @method _domCallback
* @param eventName {String} the customEvent that is transported to the eventsystem
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
Event._domCallback = function(e) {
_evCallback(e);
};
// store module:
window._ITSAmodules.EventDom = Event;
return Event;
};
},{"event":2764,"js-ext/extra/hashmap.js":2780,"js-ext/lib/array.js":2781,"js-ext/lib/object.js":2782,"js-ext/lib/string.js":2783,"polyfill/polyfill-base.js":2795,"utils":2796,"vdom":2854}],2761:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":2766,"js-ext/lib/object.js":2767,"polyfill/polyfill-base.js":2779}],2762:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":2761}],2763:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":2761,"js-ext/extra/classes.js":2765,"js-ext/lib/object.js":2767}],2764:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":2761,"./event-emitter.js":2762,"./event-listener.js":2763}],2765:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":2767,"js-ext/extra/hashmap.js":2766,"polyfill/polyfill-base.js":2770}],2766:[function(require,module,exports){
module.exports=require(4)
},{}],2767:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2766,"polyfill/polyfill-base.js":2770,"utils":2771}],2768:[function(require,module,exports){
module.exports=require(14)
},{}],2769:[function(require,module,exports){
module.exports=require(15)
},{}],2770:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2768,"./lib/window.console.js":2769}],2771:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2772,"./lib/timers.js":2773}],2772:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2766,"polyfill/polyfill-base.js":2776}],2773:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2776}],2774:[function(require,module,exports){
module.exports=require(14)
},{}],2775:[function(require,module,exports){
module.exports=require(15)
},{}],2776:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2774,"./lib/window.console.js":2775}],2777:[function(require,module,exports){
module.exports=require(14)
},{}],2778:[function(require,module,exports){
module.exports=require(15)
},{}],2779:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2777,"./lib/window.console.js":2778}],2780:[function(require,module,exports){
module.exports=require(4)
},{}],2781:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":2786}],2782:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2780,"polyfill/polyfill-base.js":2786,"utils":2787}],2783:[function(require,module,exports){
module.exports=require(29)
},{}],2784:[function(require,module,exports){
module.exports=require(14)
},{}],2785:[function(require,module,exports){
module.exports=require(15)
},{}],2786:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2784,"./lib/window.console.js":2785}],2787:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2788,"./lib/timers.js":2789}],2788:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2780,"polyfill/polyfill-base.js":2792}],2789:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2792}],2790:[function(require,module,exports){
module.exports=require(14)
},{}],2791:[function(require,module,exports){
module.exports=require(15)
},{}],2792:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2790,"./lib/window.console.js":2791}],2793:[function(require,module,exports){
module.exports=require(14)
},{}],2794:[function(require,module,exports){
module.exports=require(15)
},{}],2795:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2793,"./lib/window.console.js":2794}],2796:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2797,"./lib/timers.js":2798}],2797:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2780,"polyfill/polyfill-base.js":2801}],2798:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2801}],2799:[function(require,module,exports){
module.exports=require(14)
},{}],2800:[function(require,module,exports){
module.exports=require(15)
},{}],2801:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2799,"./lib/window.console.js":2800}],2802:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],2803:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":2807,"js-ext/extra/hashmap.js":2804,"polyfill/polyfill-base.js":2813}],2804:[function(require,module,exports){
module.exports=require(4)
},{}],2805:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":2806,"../lib/object.js":2807,"./classes.js":2803,"js-ext/extra/hashmap.js":2804,"polyfill/lib/weakmap.js":2811}],2806:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":2813}],2807:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2804,"polyfill/polyfill-base.js":2813,"utils":2814}],2808:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":2813}],2809:[function(require,module,exports){
module.exports=require(29)
},{}],2810:[function(require,module,exports){
module.exports=require(14)
},{}],2811:[function(require,module,exports){
module.exports=require(57)
},{}],2812:[function(require,module,exports){
module.exports=require(15)
},{}],2813:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2810,"./lib/window.console.js":2812}],2814:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2815,"./lib/timers.js":2816}],2815:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2804,"polyfill/polyfill-base.js":2819}],2816:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2819}],2817:[function(require,module,exports){
module.exports=require(14)
},{}],2818:[function(require,module,exports){
module.exports=require(15)
},{}],2819:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2817,"./lib/window.console.js":2818}],2820:[function(require,module,exports){
module.exports=require(66)
},{}],2821:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":2820}],2822:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":2820}],2823:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":2820}],2824:[function(require,module,exports){
module.exports=require(14)
},{}],2825:[function(require,module,exports){
module.exports=require(15)
},{}],2826:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2824,"./lib/window.console.js":2825}],2827:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2828,"./lib/timers.js":2829}],2828:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2804,"polyfill/polyfill-base.js":2832}],2829:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2832}],2830:[function(require,module,exports){
module.exports=require(14)
},{}],2831:[function(require,module,exports){
module.exports=require(15)
},{}],2832:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2830,"./lib/window.console.js":2831}],2833:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":2834}],2834:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":2835,"js-ext/lib/object.js":2836}],2835:[function(require,module,exports){
module.exports=require(4)
},{}],2836:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2835,"polyfill/polyfill-base.js":2839,"utils":2840}],2837:[function(require,module,exports){
module.exports=require(14)
},{}],2838:[function(require,module,exports){
module.exports=require(15)
},{}],2839:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2837,"./lib/window.console.js":2838}],2840:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2841,"./lib/timers.js":2842}],2841:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2835,"polyfill/polyfill-base.js":2845}],2842:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2845}],2843:[function(require,module,exports){
module.exports=require(14)
},{}],2844:[function(require,module,exports){
module.exports=require(15)
},{}],2845:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2843,"./lib/window.console.js":2844}],2846:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":2804,"js-ext/lib/object.js":2807,"js-ext/lib/string.js":2809,"polyfill":2826,"polyfill/extra/transition.js":2821,"polyfill/extra/vendorCSS.js":2823}],2847:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":2804,"js-ext/lib/object.js":2807,"polyfill":2826}],2848:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":2804,"js-ext/lib/object.js":2807,"js-ext/lib/string.js":2809,"polyfill":2826}],2849:[function(require,module,exports){
arguments[4][95][0].apply(exports,arguments)
},{"../css/element.css":2802,"./attribute-extractor.js":2846,"./element-array.js":2847,"./html-parser.js":2850,"./node-parser.js":2851,"./vdom-ns.js":2852,"./vnode.js":2853,"js-ext/extra/hashmap.js":2804,"js-ext/lib/object.js":2807,"js-ext/lib/promise.js":2808,"js-ext/lib/string.js":2809,"polyfill":2826,"polyfill/extra/transition.js":2821,"polyfill/extra/transitionend.js":2822,"polyfill/extra/vendorCSS.js":2823,"utils":2827,"window-ext":2833}],2850:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":2846,"./vdom-ns.js":2852,"js-ext/extra/hashmap.js":2804,"js-ext/lib/object.js":2807,"polyfill":2826}],2851:[function(require,module,exports){
arguments[4][97][0].apply(exports,arguments)
},{"./attribute-extractor.js":2846,"./vdom-ns.js":2852,"./vnode.js":2853,"js-ext/extra/hashmap.js":2804,"js-ext/lib/object.js":2807,"polyfill":2826}],2852:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":2804,"js-ext/lib/object.js":2807,"polyfill":2826}],2853:[function(require,module,exports){
"use strict";
/**
* Delivers the `vnode` prototype object, which is a virtualisation of an `Element` inside the Dom.
* These Elements work smoothless with the vdom (see ...).
*
* vnodes are much quicker to access and walk through than native dom-nodes. However, this is a module you don't need
* by itself: `Element`-types use these features under the hood.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* <br>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
*
* @module vdom
* @submodule vnode
* @class vnode
* @since 0.0.1
*/
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('js-ext/lib/string.js');
require('polyfill');
var createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.VNode) {
return window._ITSAmodules.VNode; // VNODE was already created
}
console.warn('VDOM 30');
var NS = require('./vdom-ns.js')(window),
extractor = require('./attribute-extractor.js')(window),
DOCUMENT = window.document,
LightMap = require('js-ext/extra/lightmap.js'),
MUTATION_EVENTS = new LightMap(),
BATCH_WILL_RUN = false,
xmlNS = NS.xmlNS,
nodeids = NS.nodeids,
htmlToVNodes = require('./html-parser.js')(window),
timers = require('utils/lib/timers.js'),
async = timers.async,
later = timers.later,
/*jshint proto:true */
PROTO_SUPPORTED = !!Object.__proto__,
/*jshint proto:false */
// cleanup memory after 1 minute: removed nodes SHOULD NOT be accessed afterwards
// because vnode would be recalculated and might be different from before
DESTROY_DELAY = 60000,
unescapeEntities = NS.UnescapeEntities,
NTH_CHILD_REGEXP = /^(?:(\d*)[n|N])([\+|\-](\d+))?$/, // an+b
STRING = 'string',
CLASS = 'class',
STYLE = 'style',
ID = 'id',
NODE= 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
EV_REMOVED = NODE+REMOVE,
EV_INSERTED = NODE+INSERT,
EV_CONTENT_CHANGE = NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = ATTRIBUTE+INSERT,
SPLIT_CHARACTER = createHashMap({
' ': true,
'>': true,
'+': true, // only select the element when it is immediately preceded by the former element
'~': true // only the element when it has the former element as a sibling. (just like `+`, but less strict)
}),
STORABLE_SPLIT_CHARACTER = createHashMap({
'>': true,
'+': true,
'~': true
}),
SIBLING_MATCH_CHARACTER = createHashMap({
'+': true,
'~': true
}),
ATTR_DETAIL_SPECIFIERS = createHashMap({
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to attribute-name end-tokens.
*
* @property END_ATTRIBUTENAME
* @default {
* '=': true,
* ']': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
END_ATTRIBUTENAME = createHashMap({
'=': true,
']': true,
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to different changes of Element nodeType changes.
*
* @property NODESWITCH
* @default {
* 1: {
* 1: 1,
* 3: 2,
* 8: 3
* },
* 3: {
* 1: 4,
* 3: 5,
* 8: 6
* },
* 8: {
* 1: 7,
* 3: 8,
* 8: 9
* }
* }
* @type Object
* @protected
* @since 0.0.1
*/
NODESWITCH = createHashMap({
1: createHashMap({
1: 1, // oldNodeType==Element, newNodeType==Element
3: 2, // oldNodeType==Element, newNodeType==TextNode
8: 3 // oldNodeType==Element, newNodeType==Comment
}),
3: createHashMap({
1: 4, // oldNodeType==TextNode, newNodeType==Element
3: 5, // oldNodeType==TextNode, newNodeType==TextNode
8: 6 // oldNodeType==TextNode, newNodeType==Comment
}),
8: createHashMap({
1: 7, // oldNodeType==Comment, newNodeType==Element
3: 8, // oldNodeType==Comment, newNodeType==TextNode
8: 9 // oldNodeType==Comment, newNodeType==Comment
})
}),
/**
* Object to gain quick access to selector start-tokens.
*
* @property SELECTOR_IDENTIFIERS
* @default {
* '#': 1,
* '.': 2,
* '[': 3
* }
* @type Object
* @protected
* @since 0.0.1
*/
SELECTOR_IDENTIFIERS = createHashMap({
'#': 1,
'.': 2,
'[': 3,
':': 4
}),
PSEUDO_FIRST_CHILD = ':first-child',
PSEUDO_FIRST_OF_TYPE = ':first-of-type',
PSEUDO_LAST_CHILD = ':last-child',
PSEUDO_LAST_OF_TYPE = ':last-of-type',
PSEUDO_NTH_CHILD = ':nth-child',
PSEUDO_NTH_LAST_CHILD = ':nth-last-child',
PSEUDO_NTH_LAST_OF_TYPE = ':nth-last-of-type',
PSEUDO_NTH_OF_TYPE = ':nth-of-type',
PSEUDO_ONLY_OF_TYPE = ':only-of-type',
PSEUDO_ONLY_CHILD = ':only-child',
/**
* Object to gain quick access to the selectors that required children
*
* @property PSEUDO_REQUIRED_CHILDREN
* @default {
* ':first-child': true,
* ':first-of-type': true,
* ':last-child': true,
* ':last-of-type': true,
* ':nth-child': true,
* ':nth-last-child': true,
* ':nth-last-of-type': true,
* ':nth-of-type': true,
* ':only-of-type': true,
* ':only-child': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
PSEUDO_REQUIRED_CHILDREN = createHashMap(),
_matchesSelectorItem, _matchesOneSelector, _findElementSibling, vNodeProto, _markRemoved, _tryReplaceChild,
_splitSelector, _findNodeSibling, _matchNthChild, _batchEmit, _emitDestroyChildren, _tryRemoveDomNode;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_CHILD] = true;
/**
* Searches for the next -or previous- node-sibling (nodeType of 1, 3 or 8).
*
* @method _findNodeSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findNodeSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
index = vParent.vChildNodes.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildNodes[index];
};
/**
* Searches for the next -or previous- Element-sibling (nodeType of 1).
*
* @method _findElementSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findElementSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
if (vnode.nodeType===1) {
index = vParent.vChildren.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildren[index];
}
else {
/*jshint noempty:true */
while ((vnode=_findNodeSibling(vnode, next)) && (vnode.nodeType!==1)) {}
/*jshint noempty:false */
return vnode;
}
};
/**
* Check whether the vnode matches a "nth-child" test, which is used for css pseudoselectors like `nth-child`, `nth-of-type` etc.
*
* @method _matchNthChild
* @param pseudoArg {String} the argument for nth-child
* @param index {Number} the index of the inspected vnode
* @return {Boolean} whether the vnode matches the nthChild test
* @protected
* @private
* @since 0.0.1
*/
_matchNthChild = function(pseudoArg, index) {
var match, k, a, b, nodeOk, nthIndex, sign, isNumber;
(pseudoArg==='even') && (pseudoArg='2n');
(pseudoArg==='odd') && (pseudoArg='2n+1');
match = pseudoArg.match(NTH_CHILD_REGEXP) || (isNumber=pseudoArg.validateNumber());
if (!match) {
return false;
}
// pseudoArg follows the pattern: `an+b`
if (!isNumber) {
a = match[1];
sign = match[2];
b = match[3] || 0;
(b==='') && (b=0);
}
else {
b = pseudoArg;
}
sign && (sign=sign[0]);
if (!a) {
// only fixed index to match
return (sign==='-') ? false : (parseInt(b, 10)===index);
}
else {
// we need to iterate
nodeOk = false;
b = window.Number(b);
for (k=0; !nodeOk; k++) {
nthIndex = (sign==='-') ? (a*k) - b : (a*k) + b;
if (nthIndex===index) {
nodeOk = true;
}
else if (nthIndex>index) {
// beyond index --> will never become a fix anymore
return false;
}
}
return nodeOk;
}
};
/**
* Check whether the vnode matches the css-selector. the css-selector should be a single selector,
* not multiple, so it shouldn't contain a `comma`.
*
* @method _matchesOneSelector
* @param vnode {vnode} the vnode to inspect
* @param selector {String} the selector-item to check the match for
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches the css-selector
* @protected
* @private
* @since 0.0.1
*/
_matchesOneSelector = function(vnode, selector, relatedVNode) {
var selList = _splitSelector(selector),
size = selList.length,
selMatch = false,
i, selectorItem, last,
rightvnode, relationMatch, checkRelation;
if (STORABLE_SPLIT_CHARACTER[selList[size-1]]) {
return false;
}
relationMatch = function(leftVNode, rightVNode, relationCharacter) {
var match, vParent, vChildren;
// when `selector` starts with `>`, `~` or `+`, then
// there should also be a match comparing a related node!
switch (relationCharacter) {
case '>':
leftVNode || (leftVNode=rightVNode.vParent);
match = (leftVNode.vChildren.indexOf(rightVNode)!==-1);
break;
case '~':
leftVNode || (leftVNode=rightVNode.vFirstElement);
vParent = leftVNode.vParent;
vChildren = vParent && vParent.vChildren;
match = vParent && (vChildren.indexOf(leftVNode)<vChildren.indexOf(rightVNode));
break;
case '+':
leftVNode || (leftVNode=rightVNode.vPreviousElement);
match = (leftVNode.vNextElement === rightVNode);
}
return !!match;
};
last = size - 1;
i = last;
while (vnode && ((selMatch || (i===last)) && (i>=0))) {
selectorItem = selList[i];
if (STORABLE_SPLIT_CHARACTER[selectorItem]) {
checkRelation = selectorItem;
i--;
}
else {
selMatch = _matchesSelectorItem(vnode, selectorItem);
if (!selMatch && (i===last)) {
return false;
}
while (!selMatch && vnode && (i!==last)) {
// may also look at nodes higher in the chain
vnode = SIBLING_MATCH_CHARACTER[selList[i+1]] ? vnode.vPreviousElement : vnode.vParent;
selMatch = vnode && _matchesSelectorItem(vnode, selectorItem);
}
selMatch && checkRelation && vnode && (selMatch=relationMatch(vnode, rightvnode, checkRelation));
rightvnode = vnode;
if (vnode) {
// do a precheck on the next checkRelation:
// and determine whether to set the vnode upper the tree or at the previous sibling:
vnode = SIBLING_MATCH_CHARACTER[selList[i-1]] ? vnode.vPreviousElement : vnode.vParent;
}
if (selMatch) {
checkRelation = false;
i--;
}
}
}
if ((rightvnode===relatedVNode) || (i!==-1)) {
selMatch = false;
}
selMatch && checkRelation && rightvnode && (selMatch=relationMatch(relatedVNode, rightvnode, checkRelation));
return selMatch;
};
/**
* Check whether the vnode matches one specific selector-item. Suppose the css-selector: "#mynode li.red .blue"
* then there are 3 selector-items: "#mynode", "li.red" and ".blue"
*
* This method also can handle the new selectors:
* <ul>
* <li>[att^=val] –-> the “begins with” selector</li>
* <li>[att$=val] –-> the “ends with” selector</li>
* <li>[att*=val] –-> the “contains” selector (might be a substring)</li>
* <li>[att~=val] –-> the “contains” selector as a separate word, separated by spaces</li>
* <li>[att|=val] –-> the “contains” selector as a separate word, separated by `|`</li>
* <li>+ --> (same level)</li>
* <li>~ --> (same level)</li>
* </ul>
*
* @method _matchesSelectorItem
* @param vnode {Object} the vnode to inspect
* @param selectorItem {String} the selector-item to check the match for
* @return {Boolean} whether the vnode matches the selector-item
* @protected
* @private
* @since 0.0.1
*/
_matchesSelectorItem = function (vnode, selectorItem) {
var i = 0,
len = selectorItem.length,
character = selectorItem[0],
tagName, id, className, attributeName, attributeValue, stringMarker, attributeisString, isBoolean, insideAttributeValue, insideAttribute,
vParent, checkBoolean, treatment, k, min, max, value, len2, index, found, pseudo, pseudoArg, arglevel, count, vParentVChildren;
if (selectorItem==='*') {
return true;
}
if (!SELECTOR_IDENTIFIERS[character]) {
// starts with tagName
tagName = '';
// reposition i to continue in the right way:
i--;
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
tagName += character;
}
if (tagName.toUpperCase()!==vnode.tag) {
return false;
}
}
while (i<len) {
switch (character) {
case '#':
id = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
id += character;
}
if (id!==vnode.id) {
return false;
}
break;
case '.':
className = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
className += character;
}
if (!vnode.hasClass(className)) {
return false;
}
break;
case '[':
attributeName = '';
while ((++i<len) && (character=selectorItem[i]) && !END_ATTRIBUTENAME[character]) {
attributeName += character;
}
// if character===']' then we have an attribute without a value-definition
if (!vnode.attrs[attributeName] || ((character===']') && (vnode.attrs[attributeName]!==''))) {
return !!vnode.attrs[attributeName];
}
// now we read the value of the attribute
// however, it could be that the selector has a special `detailed` identifier set (defined by: ATTR_DETAIL_SPECIFIERS)
if (ATTR_DETAIL_SPECIFIERS[character]) {
treatment = character; // store the character to know how the attributedata should be treaded
i++; // character should be a "=" by now
}
else {
treatment = null;
}
attributeValue = '';
stringMarker = selectorItem[i+1];
attributeisString = (stringMarker==='"') || (stringMarker==="'");
attributeisString && (i++);
// end of attributaValue = (character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker))
while ((++i<len) && (character=selectorItem[i]) && !((character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker)))) {
attributeValue += character;
}
if (attributeisString) {
// if attribute is string, then we need to _remove to last stringmarker
attributeValue = attributeValue.substr(0, attributeValue.length-1);
}
else {
// if attribute is no string, then we need to typecast its value
isBoolean = ((attributeValue.length>3) && (attributeValue.length<6) &&
(checkBoolean=attributeValue.toUpperCase()) &&
((checkBoolean==='FALSE') || (checkBoolean==='TRUE')));
// typecast the value to either Boolean or Number:
attributeValue = isBoolean ? (checkBoolean==='TRUE') : parseFloat(attributeValue);
}
// depending upon how the attributedata should be treated:
if (treatment) {
switch (treatment) {
case '^': // “begins with” selector
if (!vnode.attrs[attributeName].startsWith(attributeValue)) {
return false;
}
break;
case '$': // “ends with” selector
if (!vnode.attrs[attributeName].endsWith(attributeValue)) {
return false;
}
break;
case '*': // “contains” selector (might be a substring)
if (!vnode.attrs[attributeName].contains(attributeValue)) {
return false;
}
break;
case '~': // “contains” selector as a separate word, separated by spaces
if (!(' '+vnode.attrs[attributeName]+' ').contains(' '+attributeValue+' ')) {
return false;
}
break;
case '|': // “contains” selector as a separate word, separated by `|`
if (!('|'+vnode.attrs[attributeName]+'|').contains('|'+attributeValue+'|')) {
return false;
}
break;
}
}
else if (vnode.attrs[attributeName]!==attributeValue) {
return false;
}
// we still need to increase one position:
(++i<len) && (character=selectorItem[i]);
break;
case ':':
// we have a pseudo-selector
// first, find out which one
// because '::' is a valid start (though without any selection), we start to back the next character as well:
pseudo = ':'+selectorItem[++i];
pseudoArg = '';
vParent = vnode.vParent;
vParentVChildren = vParent && vParent.vChildren;
// pseudo-selectors might have an argument passed in, like `:nth-child(2n+1)` or `:not([type="checkbox"])` --> we
// store this argument inside `pseudoArg`
// also note that combinations are possible with `:not` --> `:not(:nth-child(2n+1))`
// also note that we cannot "just" look for a closing character when running into the usage of attributes:
// for example --> `:not([data-x="some data :)"])`
// that's why -once we are inside attribute-data- we need to continue until the attribute-data ends
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
if (character==='(') {
// starting arguments
arglevel = 1;
insideAttribute = false;
insideAttributeValue = false;
while ((++i<len) && (character=selectorItem[i]) && (arglevel>0)) {
if (!insideAttribute) {
if (character==='(') {
arglevel++;
}
else if (character===')') {
arglevel--;
}
else if (character==='[') {
insideAttribute = true;
}
}
else {
// inside attribute
if (!insideAttributeValue) {
if ((character==='"') || (character==="'")) {
insideAttributeValue = true;
stringMarker = character;
}
else if (character===']') {
insideAttribute = false;
}
}
else if ((character===stringMarker) && (selectorItem[i+1]===']')) {
insideAttributeValue = false;
}
}
(arglevel>0) && (pseudoArg+=character);
}
}
else {
pseudo += character;
}
}
// now, `pseudo` is known as well as its possible pseudoArg
if (!vParentVChildren && PSEUDO_REQUIRED_CHILDREN[pseudo]) {
return false;
}
switch (pseudo) {
case ':checked': // input:checked Selects every checked <input> element
if (!vnode.attrs.checked) {
return false;
}
break;
case ':disabled': // input:disabled Selects every disabled <input> element
if (!vnode.attrs.disabled) {
return false;
}
break;
case ':empty': // p:empty Selects every <p> element that has no children (including text nodes)
if (vnode.vChildNodes && (vnode.vChildNodes.length>0)) {
return false;
}
break;
case ':enabled': // input:enabled Selects every enabled <input> element
if (vnode.attrs.disabled) {
return false;
}
break;
case PSEUDO_FIRST_CHILD: // p:first-child Selects every <p> element that is the first child of its parent
if (vParentVChildren[0]!==vnode) {
return false;
}
break;
case PSEUDO_FIRST_OF_TYPE: // p:first-of-type Selects every <p> element that is the first <p> element of its parent
for (k=vParentVChildren.indexOf(vnode)-1; k>=0; k--) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':focus': // input:focus Selects the input element which has focus
if (vnode.domNode!==DOCUMENT.activeElement) {
return false;
}
break;
case ':in-range': // input:in-range Selects input elements with a value within a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || (value<min) || (value>max)) {
return false;
}
break;
case ':lang': // p:lang(it) Selects every <p> element with a lang attribute equal to "it" (Italian)
if (vnode.attrs.lang!==pseudoArg) {
return false;
}
break;
case PSEUDO_LAST_CHILD: // p:last-child Selects every <p> element that is the last child of its parent
if (vParentVChildren[vParentVChildren.length-1]!==vnode) {
return false;
}
break;
case PSEUDO_LAST_OF_TYPE: // p:last-of-type Selects every <p> element that is the last <p> element of its parent
len2 = vParentVChildren.length;
for (k=vParentVChildren.indexOf(vnode)+1; k<len2; k++) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':not': // :not(p) Selects every element that is not a <p> element
if (vnode.matchesSelector(pseudoArg)) {
return false;
}
break;
case PSEUDO_NTH_CHILD: // p:nth-child(2) Selects every <p> element that is the second child of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
index = vParentVChildren.indexOf(vnode)+1;
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_CHILD: // p:nth-last-child(2) Selects every <p> element that is the second child of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_OF_TYPE: // p:nth-last-of-type(2) Selects every <p> element that is the second <p> element of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
index = 0;
for (k=vParentVChildren.length-1; (k>=0) && !found; k--) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_OF_TYPE: // p:nth-of-type(2) Selects every <p> element that is the second <p> element of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
len2 = vParentVChildren.length;
index = 0;
for (k=0; (k<len2) && !found; k++) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_ONLY_OF_TYPE: // p:only-of-type Selects every <p> element that is the only <p> element of its parent
len2 = vParentVChildren.length;
count = 0;
for (k=0; (k<len2) && (count<=1); k++) {
(vParentVChildren[k].tag===vnode.tag) && count++;
}
if (count!==1) {
return false;
}
break;
case PSEUDO_ONLY_CHILD: // p:only-child Selects every <p> element that is the only child of its parent
if (vParentVChildren.length!==1) {
return false;
}
break;
case ':optional': // input:optional Selects input elements with no "required" attribute
if (vnode.attrs.required) {
return false;
}
break;
case ':out-of-range': // input:out-of-range Selects input elements with a value outside a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || ((value>=min) && (value<=max))) {
return false;
}
break;
case ':read-only': // input:read-only Selects input elements with the "readonly" attribute specified
if (!vnode.attrs.readonly) {
return false;
}
break;
case ':read-write': // input:read-write Selects input elements with the "readonly" attribute NOT specified
if (vnode.attrs.readonly) {
return false;
}
break;
case ':required': // input:required Selects input elements with the "required" attribute specified
if (!vnode.attrs.required) {
return false;
}
break;
case ':root': // Selects the document's root element
if (vnode.domNode!==DOCUMENT.documentElement) {
return false;
}
break;
}
}
}
return true;
};
/**
* Splits the selector into separate subselector-items that should match different elements through the tree.
* Special characters '>' and '+' are added as separate items in the hash.
*
* @method _splitSelector
* @param selector {String} the selector-item to check the match for
* @return {Array} splitted selectors
* @protected
* @private
* @since 0.0.1
*/
_splitSelector = function(selector) {
var list = [],
len = selector.length,
sel = '',
i, character, insideDataAttr;
for (i=0; i<len; i++) {
character = selector[i];
if (character==='[') {
sel += character;
insideDataAttr = true;
}
else if (character===']') {
sel += character;
insideDataAttr = false;
}
else if (insideDataAttr || !SPLIT_CHARACTER[character]) {
sel += character;
}
else {
// unique selectoritem is found, add it to the list
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
// in case the last character was '>', '+' or '~', we need to add it as a separate item
STORABLE_SPLIT_CHARACTER[character] && (list[list.length]=character);
}
}
// add the last item
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
return list;
};
_batchEmit = function() {
// we will exactly define the 'UI:'-event --> Itags will have another emitterName
MUTATION_EVENTS.each(function (mutationEvents, vnode) {
var domNode = vnode.domNode;
if (mutationEvents[EV_REMOVED]) {
domNode.emit('UI:'+EV_REMOVED);
}
else if (mutationEvents[EV_INSERTED]) {
domNode.emit('UI:'+EV_INSERTED);
}
else {
// contentchange and attributechanges can go hand in hand
mutationEvents.each(function(value, evt) {
domNode.emit('UI:'+evt, (evt===EV_CONTENT_CHANGE) ? null : {changed: value});
});
}
});
MUTATION_EVENTS.clear();
BATCH_WILL_RUN = false;
};
_emitDestroyChildren = function(vnode) {
var children = vnode.vChildren,
len = children.length,
i, vChild;
for (i=0; i<len; i++) {
vChild = children[i];
vChild._emit(EV_REMOVED);
}
};
_markRemoved = function(vnode) {
var vChildNodes = vnode.vChildNodes,
len, i, vChildNode;
if (vnode.nodeType===1) {
Object.protectedProp(vnode, 'removedFromDOM', true);
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && _markRemoved(vChildNode);
}
}
}
};
/**
* A safe way to remove a dom-node, even if it is not present.
* Will not throw a JS error when the "to be removed" node isn't in the dom
*
* @method _tryRemoveDomNode
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param childDomNode {DOMNode} the node that needs to be removed
* @since 0.0.1
*/
_tryRemoveDomNode = function(parentDomNode, childDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && childDomNode.isItag && childDomNode.isItag()) {
DOCUMENT._itagList.remove(childDomNode);
}
try {
parentDomNode._removeChild(childDomNode);
}
catch(err) {}
};
/**
* A safe way to replace a dom-node, even if the "to be replaced"-node it is not present.
* Will not throw a JS error when the "to be replaced" node isn't in the dom. in that case, the new node will
* be appended
*
* @method _tryReplaceChild
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param newChildDomNode {DOMNode} the node that needs to be replaced
* @param oldChildDomNode {DOMNode} the node that needs to be inserted
* @since 0.0.1
*/
_tryReplaceChild = function(parentDomNode, newChildDomNode, oldChildDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && oldChildDomNode.isItag && oldChildDomNode.isItag()) {
DOCUMENT._itagList.remove(oldChildDomNode);
}
try {
parentDomNode._replaceChild(newChildDomNode, oldChildDomNode);
}
catch(err) {
// if the childDomNode isn;t there any more - for whatever reason - the we need to append the newChildNode
parentDomNode._appendChild(newChildDomNode);
}
};
vNodeProto = window._ITSAmodules.VNode = {
/**
* Check whether the vnode's domNode is equal, or contains the specified Element.
*
* @method contains
* @return {Boolean} whether the vnode's domNode is equal, or contains the specified Element.
* @since 0.0.1
*/
contains: function(otherVNode, noProtectedSearch) {
var instance = this;
if (otherVNode && (otherVNode.destroyed || (noProtectedSearch && otherVNode._systemNode))) {
return false;
}
while (otherVNode && (otherVNode!==instance)) {
otherVNode = otherVNode.vParent;
if (otherVNode && noProtectedSearch && (otherVNode._systemNode || (otherVNode.isItag && otherVNode.domNode.contentHidden))) {
return (otherVNode===instance);
}
}
return (otherVNode===instance);
},
/**
* Empties the vnode.
*
* Syncs with the dom.
*
* @method empty
* @param [full=false] {Boolean} whether system-nodes should be removed as well
* @chainable
* @since 0.0.1
*/
empty: function(full) {
delete this._scripts;
return this._setChildNodes([], null, full);
},
/**
* Returns the first child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method firstOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the first child-vnode or null when not present
* @since 0.0.1
*/
firstOfVChildren: function(cssSelector) {
var instance = this,
found, i, len, vChildren, element;
if (!cssSelector) {
return instance.vFirstElementChild;
}
vChildren = instance.vChildren;
len = vChildren.length;
for (i=0; !found && (i<len); i++) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
return found;
},
/**
* Gets the innerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* Only valid for nodetype=1 (HTMLElements)
*
* @method getHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String|undefined} the innerHTML without the elements specified, or `undefined` when not an HTMLElement
* @since 0.0.1
*/
getHTML: function(exclude, includeSystemNodes) {
var instance = this,
html, vChildNodes, len, i, vChildNode;
if (instance.nodeType===1) {
if (exclude) {
Array.isArray(exclude) || (exclude=[exclude]);
}
html = '';
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
switch (vChildNode.nodeType) {
case 1:
(exclude && exclude.contains(vChildNode.domNode)) || (!includeSystemNodes && vChildNode._systemNode) || (html+=vChildNode.getOuterHTML(exclude, includeSystemNodes));
break;
case 3:
html += vChildNode.text.replace(/</g, '<').replace(/>/g, '>');
break;
case 8:
html += '<!--' + vChildNode.text.replace(/</g, '<').replace(/>/g, '>') + '-->';
}
}
}
return html;
},
/**
* Gets the outerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* @method getOuterHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String} the outerHTML
* @since 0.0.1
*/
getOuterHTML: function(exclude, includeSystemNodes) {
var instance = this,
html,
attrs = instance.attrs;
if (instance.nodeType===1) {
if (instance.nodeType!==1) {
return instance.textContent;
}
html = '<' + instance.tag.toLowerCase();
attrs.each(function(value, key) {
html += ' '+key+'="'+value+'"';
});
instance.isVoid && (html += '/');
html += '>';
if (!instance.isVoid) {
html += ((!includeSystemNodes && instance.isItag) ? '' : instance.getHTML(exclude, includeSystemNodes)) + '</' + instance.tag.toLowerCase() + '>';
}
}
return html;
},
/**
* Checks whether the vnode has any vChildNodes (nodeType of 1, 3 or 8).
*
* @method hasVChildNodes
* @return {Boolean} whether the vnode has any vChildNodes.
* @since 0.0.1
*/
hasVChildNodes: function() {
return this.vChildNodes ? (this.vChildNodes.length>0) : false;
},
/**
* Checks whether the vnode has any vChildren (vChildNodes with nodeType of 1).
*
* @method hasVChildren
* @return {Boolean} whether the vnode has any vChildren.
* @since 0.0.1
*/
hasVChildren: function() {
return this.vChildNodes ? (this.vChildren.length>0) : false;
},
/**
* Checks whether the className is present on the vnode.
*
* @method hasClass
* @param className {String|Array} the className to check for. May be an Array of classNames, which all needs to be present.
* @return {Boolean} whether the className (or classNames) is present on the vnode
* @since 0.0.1
*/
hasClass: function(className) {
var instance = this,
check = function(cl) {
return !!instance.classNames[cl];
};
if (!instance.classNames) {
return false;
}
if (typeof className === STRING) {
return check(className);
}
else if (Array.isArray(className)) {
return className.every(check);
}
return false;
},
/**
* Returns the last child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method lastOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the last child-vnode or null when not present
* @since 0.0.1
*/
lastOfVChildren: function(cssSelector) {
var vChildren = this.vChildren,
found, i, element;
if (vChildren) {
if (!cssSelector) {
return this.vLastElementChild;
}
for (i=vChildren.length-1; !found && (i>=0); i--) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
}
return found;
},
/**
* Checks whether the vnode matches one of the specified selectors. `selectors` can be one, or multiple css-selectors,
* separated by a `comma`. For example: "#myid li.red blue" is one selector, "div.red, div.blue, div.green" are three selectors.
*
* @method matchesSelector
* @param selectors {String} one or more css-selectors
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches one of the selectors
* @since 0.0.1
*/
matchesSelector: function(selectors, relatedVNode) {
var instance = this;
if (instance.nodeType!==1) {
return false;
}
selectors = selectors.split(',');
// we can use Array.some, because there won't be many separated selectoritems,
// so the final invocation won't be delayed much compared to looping
return selectors.some(function(selector) {
return _matchesOneSelector(instance, selector, relatedVNode);
});
},
/**
* Reloads the DOM-attribute into the vnode.
*
* @method matchesSelector
* @param attributeName {String} the name of the attribute to be reloaded.
* @return {Node} the domNode that was reloaded.
* @since 0.0.1
*/
reloadAttr: function(attributeName) {
var instance = this,
domNode = instance.domNode,
attributeValue = domNode._getAttribute(attributeName),
attrs = instance.attrs,
extractStyle, extractClass;
if (instance.nodeType===1) {
attributeValue || (attributeValue='');
if (attributeValue==='') {
delete attrs[attributeName];
// in case of STYLE attributeName --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attributeName --> special treatment
(attributeName===CLASS) && (instance.classNames={});
// in case of ID attributeName --> special treatment
if ((attributeName===ID) && (instance.id)) {
delete nodeids[instance.id];
delete instance.id;
}
}
else {
attrs[attributeName] = attributeValue;
// in case of STYLE attributeName --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(attributeValue);
attributeValue = extractStyle.attrStyle;
if (attributeValue) {
attrs.style = attributeValue;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attributeName --> special treatment
extractClass = extractor.extractClass(attributeValue);
attributeValue = extractClass.attrClass;
if (attributeValue) {
attrs[CLASS] = attributeValue;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (instance.id!==attributeValue) && (delete nodeids[instance.id]);
instance.id = attributeValue;
nodeids[attributeValue] = domNode;
}
}
}
return domNode;
},
/**
* Returns the vnode's style in a serialized form: the way it appears in the dom.
*
* @method serializeStyles
* @return {String} vnode's style
* @since 0.0.1
*/
serializeStyles: function() {
return extractor.serializeStyles(this.styles);
},
/**
* Sets the vnode's and dom-nodes inner HTML.
*
* Syncs with the dom. Can be invoked multiple times without issues.
*
* @method setHTML
* @param content {String} the innerHTML
* @param [suppressItagRender] {Boolean} to suppress Itags from rendering
* @param [allowScripts=false] {Boolean} whether scripts are allowed --> these should be defined with `xscript` instead of `script`
* @chainable
* @since 0.0.1
*/
setHTML: function(content, suppressItagRender, allowScripts) {
var instance = this;
instance._setChildNodes(htmlToVNodes(content, vNodeProto, instance.ns, null, suppressItagRender, allowScripts), suppressItagRender);
return instance;
},
/**
* Syncs the vnode's nodeid (if available) inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom. Can be invoked multiple times without issues.
*
* @method storeId
* @chainable
* @since 0.0.1
*/
storeId: function() {
// store node/vnode inside WeakMap:
var instance = this;
instance.id ? (nodeids[instance.id]=instance.domNode) : (delete nodeids[instance.id]);
return instance;
},
//---- private ------------------------------------------------------------------
_addToTaglist: function() {
var instance = this,
itagList;
if (instance.isItag) {
itagList = DOCUMENT.getItags(); // also reads the dom if the list isn't build yet
instance._data || Object.protectedProp(instance, '_data', {});
if (!instance._data.ce_destroyed && !itagList.contains(instance.domNode)) {
itagList.push(instance.domNode);
}
}
},
/**
* Adds a vnode to the end of the list of vChildNodes.
*
* Syncs with the DOM.
*
* @method _appendChild
* @param VNode {vnode} vnode to append
* @private
* @return {Node} the Node that was appended
* @since 0.0.1
*/
_appendChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
size, noProccessScript, scriptContent;
if ((VNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (VNode.attrs && VNode.attrs.src) ? VNode.attrs.src : VNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
VNode._moveToParent(instance);
instance.domNode._appendChild(domNode);
if (VNode.nodeType===3) {
size = instance.vChildNodes.length;
instance._normalize();
// if the size changed, then the domNode was merged
(size===instance.vChildNodes.length) || (domNode=instance.vChildNodes[instance.vChildNodes.length-1].domNode);
}
if (VNode.nodeType===1) {
VNode._addToTaglist();
VNode._emit(EV_INSERTED);
}
return domNode;
}
},
/**
* Removes the vnode from its parent vChildNodes- and vChildren-list.
*
* Does NOT sync with the dom.
*
* @method _deleteFromParent
* @private
* @chainable
* @since 0.0.1
*/
_deleteFromParent: function() {
var instance = this,
vParent = instance.vParent;
if (vParent && vParent.vChildNodes) {
vParent.vChildNodes.remove(instance);
// force to recalculate the vChildren on a next call:
(instance.nodeType===1) && (vParent._vChildren=null);
}
return instance;
},
/**
* Cleans up (empties) vnode's `_data`
*
* @method _cleanData
* @private
* @chainable
* @since 0.0.1
*/
_cleanData: function() {
var instance = this,
data = instance._data;
data && data.each(
function(value, key) {
delete data[key];
}
);
return instance;
},
/**
* Cleans up (removes) duplicated `style` definitions.
*
* @method _cleanupStyle
* @private
* @chainable
* @since 0.0.1
*/
_cleanupStyle: function() {
var instance = this,
compare = [],
removal = [],
vChildren = instance.vChildren,
len = vChildren.length,
vChild, i, styleContent, vChildInner;
for (i=0; i<len; i++) {
vChild = vChildren[i];
if (vChild.tag==='STYLE') {
vChildInner = vChild.vChildNodes[0];
if (vChildInner) {
styleContent = vChildInner.text;
if (compare.contains(styleContent)) {
removal[removal.length] = vChild;
}
else {
compare[compare.length] = styleContent;
}
}
else {
// empty style-tag
removal[removal.length] = vChild;
}
}
}
removal.forEach(function(vnode) {
instance._removeChild(vnode);
});
},
/**
* Destroys the vnode and all its vnode-vChildNodes.
* Removes it from its vParent.vChildNodes list,
* also removes its definitions inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom.
*
* @method _destroy
* @private
* @chainable
* @since 0.0.1
*/
_destroy: function(silent) {
var instance = this,
vChildNodes = instance.vChildNodes,
len, i, vChildNode, vParent, treeNodes;
if (!instance.destroyed) {
if (!silent) {
// Because we don't want to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
later(function() {
instance._emit(EV_REMOVED, null, null, null, true);
}, 5);
}
Object.protectedProp(instance, 'destroyed', true);
// first: determine the dom-tree, which module `event-dom` needs to determine where the node was before it was destroyed:
treeNodes = [instance];
vParent = instance.vParent;
while (vParent) {
treeNodes[treeNodes.length] = vParent;
vParent = vParent.vParent;
}
// mark all its vChildNodes so we can see if the node is in the DOM
_markRemoved(instance);
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && instance.isItag) {
DOCUMENT._itagList.remove(instance.domNode);
}
instance._scripts && (instance._scripts.length=0);
// The definite cleanup needs to be done after a timeout:
// someone might need to handle the Element when removed (fe to cleanup specific things)
later(function() {
instance._cleanData();
if (instance.nodeType===1) {
// _destroy all its vChildNodes
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && vChildNode._destroy(true);
}
}
}
instance._vChildren = null;
// explicitely set instance.domNode._vnode and instance.domNode to null in order to prevent problems with the GC (we break the circular reference)
delete instance.domNode._vnode;
// if valid id, then _remove the DOMnodeRef from internal hash
instance.id && delete nodeids[instance.id];
}, silent ? 0 : DESTROY_DELAY);
instance._deleteFromParent();
// Do not make domNode `null` --> it could be used even when not in the dom
}
return instance;
},
_emit: function(evt, attribute, newValue, prevValue, destroyEvt) {
/**
* Emitted by every Element that gets inserted.
*
* @event nodeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets removed.
*
* @event noderemove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets its content changed (innerHTML/innerText).
*
* @event nodecontentchange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute inserted.
*
* @event attributeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* </ul>
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute removed.
*
* @event attributeremove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Strings of the attributeNames that are removed
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute changed.
*
* @event attributechange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* <li>prevValue</li>
* </ul>
* @since 0.1
*/
var instance = this,
silent, attrMutations, mutationEvents, mutation, vParent;
if (!DOCUMENT.hasMutationSubs || (instance.nodeType!==1)) {
return;
}
silent = !!DOCUMENT._suppressMutationEvents;
if (!silent && (destroyEvt || !instance.destroyed)) {
// Because we don't wannt to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
mutationEvents = MUTATION_EVENTS.get(instance) || {};
if (attribute) {
attrMutations = mutationEvents[evt] || [];
if (evt===EV_ATTRIBUTE_REMOVED) {
mutation = attribute;
}
else {
mutation = {
attribute: attribute
};
if ((evt===EV_ATTRIBUTE_INSERTED) || (evt===EV_ATTRIBUTE_CHANGED)) {
mutation.newValue = newValue;
}
if ((evt===EV_ATTRIBUTE_CHANGED) && prevValue) {
mutation.prevValue = prevValue;
}
}
attrMutations.push(mutation);
mutationEvents[evt] = attrMutations;
}
else {
mutationEvents[evt] = true;
}
MUTATION_EVENTS.set(instance, mutationEvents);
// now set all parent to have a nodecontentchange:
vParent = instance.vParent;
vParent && vParent._emit(EV_CONTENT_CHANGE);
// in case of removal we need to emit EV_REMOVED for all children right now
// for they will be actually removed silently after a delay of 1 minute
(evt===EV_REMOVED) && _emitDestroyChildren(instance);
if (!BATCH_WILL_RUN) {
BATCH_WILL_RUN = true;
async(function() {
_batchEmit();
});
}
}
return instance;
},
/**
* Inserts `newVNode` before `refVNode`.
*
* Syncs with the DOM.
*
* @method _insertBefore
* @param newVNode {vnode} vnode to insert
* @param refVNode {vnode} The vnode before which newVNode should be inserted.
* @private
* @return {Node} the Node being inserted (equals domNode)
* @since 0.0.1
*/
_insertBefore: function(newVNode, refVNode) {
var instance = this,
domNode = newVNode.domNode,
index = instance.vChildNodes.indexOf(refVNode),
noProccessScript, scriptContent;
if ((newVNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (newVNode.attrs && newVNode.attrs.src) ? newVNode.attrs.src : newVNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
if (index!==-1) {
newVNode._moveToParent(instance, index);
instance.domNode._insertBefore(domNode, refVNode.domNode);
(newVNode.nodeType===3) && instance._normalize();
if (newVNode.nodeType===1) {
newVNode._addToTaglist();
newVNode._emit(EV_INSERTED);
}
return domNode;
}
else {
console.warn('trying to insert before, but no ref-node found. Will append the new node');
return instance._appendChild(newVNode);
}
}
},
/**
* Moves the vnode from its current parent.vChildNodes list towards a new parent vnode at the specified position.
*
* Does NOT sync with the dom.
*
* @method _moveToParent
* @param parentVNode {vnode} the parent-vnode
* @param [index] {Number} the position of the child. When not specified, it will be appended.
* @private
* @chainable
* @since 0.0.1
*/
_moveToParent: function(parentVNode, index) {
var instance = this,
vParent = instance.vParent;
instance._deleteFromParent();
instance.vParent = parentVNode;
parentVNode.vChildNodes || (parentVNode.vChildNodes=[]);
(typeof index==='number') ? parentVNode.vChildNodes.insertAt(instance, index) : (parentVNode.vChildNodes[parentVNode.vChildNodes.length]=instance);
// force to recalculate the vChildren on a next call:
vParent && (instance.nodeType===1) && (vParent._vChildren = null);
// force to recalculate the vChildren on a next call:
parentVNode && (instance.nodeType===1) && (parentVNode._vChildren=null);
return instance;
},
/**
* Removes empty TextNodes and merges following TextNodes inside the vnode.
*
* Syncs with the dom.
*
* @method _normalize
* @private
* @chainable
* @since 0.0.1
*/
_normalize: function() {
var instance = this,
domNode = instance.domNode,
vChildNodes = instance.vChildNodes,
changed = false,
i, preChildNode, vChildNode;
if (!instance._unNormalizable && vChildNodes) {
for (i=vChildNodes.length-1; i>=0; i--) {
vChildNode = vChildNodes[i];
preChildNode = vChildNodes[i-1]; // i will get the value `-1` eventually, which leads into undefined preChildNode
if (vChildNode.nodeType===3) {
if (vChildNode.text==='') {
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
else if (preChildNode && preChildNode.nodeType===3) {
preChildNode.text += vChildNode.text;
preChildNode.domNode.nodeValue = unescapeEntities(preChildNode.text);
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
}
}
}
changed && instance._emit(EV_CONTENT_CHANGE);
return instance;
},
/**
* Makes the vnode `normalizable`. Could be set to `false` when batch-inserting nodes, while `normalizaing` manually at the end.
* Afterwards, you should always reset `normalizable` to true.
*
* @method _normalizable
* @param value {Boolean} whether the vnode should be normalisable.
* @private
* @chainable
* @since 0.0.1
*/
_normalizable: function(value) {
var instance = this;
value ? (delete instance._unNormalizable) : (instance._unNormalizable=true);
return instance;
},
/**
* Prevents MutationObserver from making the dom sync with the vnode.
* Should be used when manipulating the dom from within the vnode itself (to preventing looping)
*
* @method _noSync
* @chainable
* @private
* @since 0.0.1
*/
_noSync: function() {
var instance = this;
if (!instance._nosync) {
instance._nosync = true;
async(function() {
instance._nosync = false;
});
}
return instance;
},
/**
* Removes the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _removeAttr
* @param attributeName {String}
* @private
* @chainable
* @since 0.0.1
*/
_removeAttr: function(attributeName, suppressItagRender) {
var instance = this,
attributeNameSplitted, ns;
if (instance.isItag && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
console.warn('Not allowed to remove the attribute '+attributeName);
return instance;
}
if (instance.attrs[attributeName]!==undefined) {
delete instance.attrs[attributeName];
// in case of STYLE attribute --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attribute --> special treatment
(attributeName===CLASS) && (instance.classNames={});
if (attributeName===ID) {
delete nodeids[instance.id];
delete instance.id;
}
instance._emit(EV_ATTRIBUTE_REMOVED, attributeName);
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
instance.domNode._removeAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName);
}
else {
instance.domNode._removeAttribute(attributeName);
}
}
return instance;
},
/**
* Removes the vnode's child-vnode from its vChildren and the DOM.
*
* Syncs with the DOM.
*
* @method removeChild
* @param VNode {vnode} the child-vnode to remove
* @private
* @since 0.0.1
*/
_removeChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
hadFocus = domNode && domNode.hasFocus && domNode.hasFocus() && (VNode.attrs['fm-lastitem']==='true'),
parentVNode = VNode.vParent;
VNode._destroy();
_tryRemoveDomNode(instance.domNode, VNode.domNode);
instance._normalize();
// now, reset the focus on focusmanager when needed:
if (hadFocus) {
while (parentVNode && !parentVNode.attrs['fm-manage']) {
parentVNode = parentVNode.vParent;
}
parentVNode && parentVNode.domNode.focus();
}
},
/**
* Replaces the current vnode at the parent.vChildNode list by `newVNode`
*
* Does NOT sync with the dom.
*
* @method _replaceAtParent
* @param newVNode {Object} the new vnode which should take over the place of the current vnode
* @private
* @chainable
* @since 0.0.1
*/
_replaceAtParent: function(newVNode) {
var instance = this,
vParent = instance.vParent,
vChildNodes, index;
if (vParent && (vChildNodes=vParent.vChildNodes)) {
index = vChildNodes.indexOf(instance);
// force to recalculate the vChildren on a next call:
((instance.nodeType===1) || (newVNode.nodeType===1)) && (instance.vParent._vChildren=null);
vChildNodes[index] = newVNode;
}
return instance._destroy();
},
/**
* Sets the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _setAttr
* @param attributeName {String}
* @param value {String} the value for the attributeName
* @param [force=false] {Boolean} force the attribute to be set, even if restrictions would deny it
* @private
* @chainable
* @since 0.0.1
*/
_setAttr: function(attributeName, value, force, suppressItagRender) {
var instance = this,
extractStyle, extractClass,
attrs = instance.attrs,
prevVal = attrs[attributeName],
domNode = instance.domNode,
attributeNameSplitted, ns;
if (instance.isItag && !force && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
if (prevVal!=value) {
console.warn('Not allowed to set the attribute '+attributeName);
}
return instance;
}
// don't check by !== --> value isn't parsed into a String yet
if (prevVal && ((value===undefined) || (value===null))) {
instance._removeAttr(attributeName, suppressItagRender);
return instance;
}
// attribute-values are always Strings:
value = String(value);
// attribute-values will be stored without " or '
value = value.replace(/"/g, '"').replace(/'/g, "'");
if (prevVal!=value) {
attrs[attributeName] = value;
// in case of STYLE attribute --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(value);
value = extractStyle.attrStyle;
if (value) {
attrs.style = value;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attribute --> special treatment
extractClass = extractor.extractClass(value);
value = extractClass.attrClass;
if (value) {
attrs[CLASS] = value;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (delete nodeids[instance.id]);
instance.id = value;
nodeids[value] = domNode;
}
instance._emit(prevVal ? EV_ATTRIBUTE_CHANGED : EV_ATTRIBUTE_INSERTED, attributeName, value, prevVal);
// when set in the dom --> quotes need to be set as "
value = value.replace(/"/g, '"');
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
domNode._setAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName, value);
}
else {
domNode._setAttribute(attributeName, value);
}
}
return instance;
},
/**
* Redefines the attributes of both the vnode as well as its related dom-node. The new
* definition replaces any previous attributes (without touching unmodified attributes).
* the `is` attribute cannot be changed for itags.
*
* Syncs the new vnode's attributes with the dom.
*
* @method _setAttrs
* @param newAttrs {Object|Array} the new attributes to be set
* @private
* @chainable
* @since 0.0.1
*/
_setAttrs: function(newAttrs, suppressItagRender) {
// does sync the DOM
var instance = this,
attrsObj, attr, attrs, i, key, keys, len, value;
if (instance.nodeType!==1) {
return;
}
instance._noSync();
attrs = instance.attrs;
attrs.id && (delete nodeids[attrs.id]);
if (Object.isObject(newAttrs)) {
attrsObj = newAttrs;
}
else {
attrsObj = {};
len = newAttrs.length;
for (i=0; i<len; i++) {
attr = newAttrs[i];
attrsObj[attr.name] = attr.value;
}
}
if (instance.isItag && !suppressItagRender) {
if (attrs.is) {
attrsObj.is = attrs.is;
}
else {
delete attrsObj.is;
delete attrsObj.Is;
delete attrsObj.iS;
delete attrsObj.IS;
}
}
// first _remove the attributes that are no longer needed.
// quickest way for object iteration: http://jsperf.com/object-keys-iteration/20
keys = Object.keys(attrs);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
attrsObj[key] || instance._removeAttr(key, suppressItagRender);
}
// next: every attribute that differs: redefine
keys = Object.keys(attrsObj);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
value = attrsObj[key];
(attrs[key]===value) || instance._setAttr(key, value, suppressItagRender);
}
return instance;
},
/**
* Redefines the childNodes of both the vnode as well as its related dom-node. The new
* definition replaces any previous nodes. (without touching unmodified nodes).
*
* Syncs the new vnode's childNodes with the dom.
*
* @method _setChildNodes
* @param newVChildNodes {Array} array with vnodes which represent the new childNodes
* @private
* @chainable
* @since 0.0.1
*/
_setChildNodes: function(newVChildNodes, suppressItagRender, removeSystemElements) {
// does sync the DOM
var instance = this,
vChildNodes = instance.vChildNodes || [],
domNode = instance.domNode,
forRemoval = [],
i, oldChild, newChild, newLength, len, len2, childDomNode, nodeswitch, bkpAttrs, cleanupStyle,
process, bkpChildNodes, needNormalize, prevSuppress, scriptContent, _scripts, scriptLen, j;
instance._noSync();
// first: reset ._vChildren --> by making it empty, its getter will refresh its list on a next call
instance._vChildren = null;
// if newVChildNodes is undefined, then we assume it to be empty --> an empty array
newVChildNodes || (newVChildNodes=[]);
// quickest way to loop through array is by using for loops: http://jsperf.com/array-foreach-vs-for-loop/5
len = vChildNodes.length;
// we need to add the systemNodes -if any- to the new newVChildNodes: they should be retained.
// SystemNodes are always places at the beginning, so they won't get reshuffled:
if (!removeSystemElements) {
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
if (oldChild._systemNode) {
newVChildNodes.insertAt(oldChild, i);
}
else {
break;
}
}
}
newLength = newVChildNodes.length;
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
childDomNode = oldChild.domNode;
if (i < newLength) {
newChild = newVChildNodes[i];
newChild.vParent || (newChild.vParent=instance);
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
/*jshint boss:true */
switch (nodeswitch=NODESWITCH[oldChild.nodeType][newChild.nodeType]) {
/*jshint boss:false */
case 1: // oldNodeType==Element, newNodeType==Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
if ((oldChild.tag!==newChild.tag) ||
((oldChild.tag===newChild.tag) && oldChild.isItag && (oldChild.attrs.is!==newChild.attrs.is))) {
// new tag --> completely replace
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
/*jshint proto:true */
oldChild.isItag && oldChild.domNode.destroyUI && oldChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
newChild.attrs = {}; // reset to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
oldChild._replaceAtParent(newChild);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
else {
// same tag --> only update what is needed
// NOTE: when this._unchangableAttrs exists, an itag-element syncs its UI -->
if (oldChild._data) {
// we might need to set the class `itag-rendered` when the attributeData says so:
// this happens when an itag gets refreshed with an unrendered definition
if (oldChild._data.itagRendered && !newChild.hasClass('itag-rendered')) {
newChild.classNames['itag-rendered'] = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' '+'itag-rendered';
}
else {
newChild.attrs[CLASS] = 'itag-rendered';
}
}
// we might need to set the class `focussed` when the attributeData says so:
// this happens when an itag gets rerendered: its renderFn doesn't know if any elements
// were focussed
if (oldChild._data.focussed && !newChild.hasClass('focussed')) {
newChild.classNames.focussed = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' ' + 'focussed';
}
else {
newChild.attrs[CLASS] = 'focussed';
}
}
if (oldChild._data['fm-tabindex']) {
// node has the tabindex set by the focusmanager,
// but that info might got lost with re-rendering of the new element
newChild.attrs.tabindex = '0';
}
}
if (oldChild.isItag) {
prevSuppress = DOCUMENT._suppressMutationEvents || false;
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
/*jshint proto:true */
newChild.domNode.destroyUI && newChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
oldChild._setAttrs(newChild.attrs, suppressItagRender);
newChild._destroy(true); // destroy through the vnode and removing from DOCUMENT._itagList
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(prevSuppress);
}
else {
oldChild._setAttrs(newChild.attrs, suppressItagRender);
// next: sync the vChildNodes:
oldChild._setChildNodes(newChild.vChildNodes, suppressItagRender);
}
// reset ref. to the domNode, for it might have been changed by newChild:
oldChild.id && (nodeids[oldChild.id]=childDomNode);
newVChildNodes[i] = oldChild;
}
}
break;
case 2: // oldNodeType==Element, newNodeType==TextNode
// case2 and case3 should be treated the same
case 3: // oldNodeType==Element, newNodeType==Comment
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
oldChild._replaceAtParent(newChild);
instance._emit(EV_CONTENT_CHANGE);
break;
case 4: // oldNodeType==TextNode, newNodeType==Element
// case4 and case7 should be treated the same
case 7: // oldNodeType==Comment, newNodeType==Element
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
// oldChild.isVoid = newChild.isVoid;
// delete oldChild.text;
instance._emit(EV_CONTENT_CHANGE);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
break;
case 5: // oldNodeType==TextNode, newNodeType==TextNode
// case5 and case9 should be treated the same
case 9: // oldNodeType==Comment, newNodeType==Comment
if (oldChild.text!==newChild.text) {
oldChild.text = newChild.text;
oldChild.domNode.nodeValue = unescapeEntities(newChild.text);
instance._emit(EV_CONTENT_CHANGE);
}
newVChildNodes[i] = oldChild;
break;
case 6: // oldNodeType==TextNode, newNodeType==Comment
// case6 and case8 should be treated the same
case 8: // oldNodeType==Comment, newNodeType==TextNode
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = oldChild.vParent;
instance._emit(EV_CONTENT_CHANGE);
}
if ((nodeswitch===2) || (nodeswitch===5) || (nodeswitch===8)) {
needNormalize = true;
}
}
else {
// _remove previous definition
_tryRemoveDomNode(domNode, oldChild.domNode);
// the oldChild needs to be removed, however, this cannot be done right now, for it would effect the loop
// so we store it inside a hash to remove it later
forRemoval[forRemoval.length] = oldChild;
}
}
// now definitely remove marked childNodes:
len2 = forRemoval.length;
for (i=0; i<len2; i++) {
forRemoval[i]._destroy();
}
// now we add all new vChildNodes that go beyond `len`:
for (i = len; i < newLength; i++) {
newChild = newVChildNodes[i];
newChild.vParent = instance;
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
switch (newChild.nodeType) {
case 1: // Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
domNode._appendChild(newChild.domNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
break;
case 3: // TextNode
needNormalize = true;
// we need to break through --> no `break`
/* falls through */
default: // TextNode or CommentNode
// newChild.domNode.nodeValue = newChild.text;
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
domNode._appendChild(newChild.domNode);
instance._emit(EV_CONTENT_CHANGE);
}
newChild.storeId();
}
instance.vChildNodes = newVChildNodes;
needNormalize && instance._normalize();
cleanupStyle && instance._cleanupStyle();
return instance;
},
_setUnchangableAttrs: function(unchangableObj) {
this._unchangableAttrs = unchangableObj;
}
};
//---- properties ------------------------------------------------------------------
/**
* A hash of all the `attributes` of the vnode's representing dom-node.
*
* @property attrs
* @type Object
* @since 0.0.1
*/
/**
* Hash with all the classes of the vnode. Every class represents a key, all values are set `true`.
*
* @property classNames
* @type Object
* @since 0.0.1
*/
/**
* The `id` of the vnode's representing dom-node (if any).
*
* @property id
* @type String
* @since 0.0.1
*/
/**
* Tells whether tag is a void Element. Examples are: `br`, `img` and `input`. Non-void Elements are f.e. `div` and `table`.
* For TextNodes and CommentNodes, this property is `undefined`.
*
* @property isVoid
* @type Boolean
* @since 0.0.1
*/
/**
* The `nodeType` of the vnode's representing dom-node (1===ElementNode, 3===TextNode, 8===CommentNode).
*
* @property nodeType
* @type Number
* @since 0.0.1
*/
/**
* The `tag` of the vnode's representing dom-node (allways uppercase).
*
* @property tag
* @type String
* @since 0.0.1
*/
/**
* The `content` of the vnode's representing dom-node, in case it is a TextNode or CommentNode.
* Equals dom-node.nodeValue.
*
* Is `undefined` for ElementNodes.
*
* @property text
* @type String
* @since 0.0.1
*/
/**
* Hash with all the childNodes (vnodes). vChildNodes are any kind of vnodes (nodeType===1, 3 or 8)
*
* @property vChildNodes
* @type Array
* @since 0.0.1
*/
/**
* The underlying `dom-node` that the vnode represents.
*
* @property domNode
* @type domNode
* @since 0.0.1
*/
/**
* vnode's parentNode (defined as a vnode itself).
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
Object.defineProperties(vNodeProto, {
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property innerHTML
* @type String
* @since 0.0.1
*/
innerHTML: {
get: function() {
return this.getHTML();
},
set: function(v) {
this.setHTML(v);
}
},
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property nodeValue
* @type String
* @since 0.0.1
*/
nodeValue: {
get: function() {
var instance = this;
return ((instance.nodeType===3) || (instance.nodeType===8)) ? instance.text : null;
},
set: function(v) {
var instance = this,
newTextContent, prevTextContent;
if ((instance.nodeType===3) || (instance.nodeType===8)) {
prevTextContent = instance.domNode.textContent;
instance.domNode.textContent = v;
// set .text AFTER the dom-node is updated --> the content might be escaped!
newTextContent = instance.text = instance.domNode.textContent;
(newTextContent!==prevTextContent) && instance._emit(EV_CONTENT_CHANGE);
}
}
},
/**
* Gets or sets the outerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property outerHTML
* @type String
* @since 0.0.1
*/
outerHTML: {
get: function() {
return this.getOuterHTML();
},
set: function(v) {
var instance = this,
vParent = instance.vParent,
id = instance.attrs.id,
vnode, vnodes, bkpAttrs, bkpChildNodes, i, len, vChildNodes, isLastChildNode, index, refDomNode;
if ((instance.nodeType!==1) || !vParent) {
return;
}
instance._noSync();
vChildNodes = vParent.vChildNodes;
index = vChildNodes.indexOf(instance);
isLastChildNode = (index===(vChildNodes.length-1));
isLastChildNode || (refDomNode=vChildNodes[index+1].domNode);
vnodes = htmlToVNodes(v, vNodeProto, vParent.ns, vParent);
len = vnodes.length;
if (len>0) {
// the first vnode will replace the current instance:
vnode = vnodes[0];
if (vnode.nodeType===1) {
if (vnode.tag!==instance.tag) {
// new tag --> completely replace
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
id && (delete nodeids[id]);
vnode.attrs = {}; // reset to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
// vnode.attrs = bkpAttrs;
// vnode.vChildNodes = bkpChildNodes;
vnode.id && (nodeids[vnode.id]=vnode.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
else {
instance._setAttrs(vnode.attrs);
instance._setChildNodes(vnode.vChildNodes);
}
}
else {
id && (delete nodeids[id]);
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
}
for (i=1; i<len; i++) {
vnode = vnodes[i];
switch (vnode.nodeType) {
case 1: // Element
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
vnode.attrs = {}; // reset, to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._insertBefore(vnode.domNode, refDomNode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
break;
default: // TextNode or CommentNode
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._appendChild(vnode.domNode, refDomNode);
}
vnode.storeId();
vnode._moveToParent(vParent, index+i);
}
}
},
/**
* Gets or sets the innerContent of the Node as plain text.
*
* The setter syncs with the DOM.
*
* @property textContent
* @type String
* @since 0.0.1
*/
textContent: {
get: function() {
var instance = this,
text = '',
vChildNodes = instance.vChildNodes,
len, i, vChildNode;
if (instance.nodeType===1) {
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
text += (vChildNode.nodeType===3) ? vChildNode.text : ((vChildNode.nodeType===1) ? vChildNode.textContent : '');
}
}
else {
text = instance.text;
}
return text;
},
set: function(v) {
var vnode = Object.create(vNodeProto);
vnode.domNode = DOCUMENT.createTextNode(v);
// create circular reference:
vnode.domNode._vnode = vnode;
vnode.nodeType = 3;
vnode.text = vnode.domNode.textContent;
this._setChildNodes([vnode]);
}
},
/**
* Hash with all the children (vnodes). vChildren are vnodes that have a representing dom-node that is an HtmlElement (nodeType===1)
*
* @property vChildren
* @type Array
* @since 0.0.1
*/
vChildren: {
get: function() {
var instance = this,
children = instance._vChildren,
vChildNode, vChildNodes, i, len;
vChildNodes = instance.vChildNodes;
if (vChildNodes && !children) {
children = instance._vChildren = [];
len = vChildNodes.length;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
(vChildNode.nodeType===1) && (children[children.length]=vChildNode);
}
}
children || (children = instance._vChildren = []);
return children;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirst
* @type vnode
* @since 0.0.1
*/
vFirst: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstChild;
}
},
/**
* Reference to the first vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirstChild
* @type vnode
* @since 0.0.1
*/
vFirstChild: {
get: function() {
return (this.vChildNodes && this.vChildNodes[0]) || null;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vFirstElement
* @type vnode
* @since 0.0.1
*/
vFirstElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstElementChild;
}
},
/**
* Reference to the first vChild, where the related dom-node an Element (nodeType===1).
*
* @property vFirstElementChild
* @type vnode
* @since 0.0.1
*/
vFirstElementChild: {
get: function() {
return this.vChildren[0] || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLast
* @type vnode
* @since 0.0.1
*/
vLast: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastChild;
}
},
/**
* Reference to the last vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLastChild
* @type vnode
* @since 0.0.1
*/
vLastChild: {
get: function() {
var vChildNodes = this.vChildNodes;
return (vChildNodes && vChildNodes[vChildNodes.length-1]) || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vLastElement
* @type vnode
* @since 0.0.1
*/
vLastElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastElementChild;
}
},
/**
* Reference to the last vChild, where the related dom-node an Element (nodeType===1).
*
* @property vLastElementChild
* @type vnode
* @since 0.0.1
*/
vLastElementChild: {
get: function() {
var vChildren = this.vChildren;
return vChildren[vChildren.length-1] || null;
}
},
/**
* the Parent vnode
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
/**
* Reference to the next of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vNext
* @type vnode
* @since 0.0.1
*/
vNext: {
get: function() {
return _findNodeSibling(this, true);
}
},
/**
* Reference to the next of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vNextElement
* @type vnode
* @since 0.0.1
*/
vNextElement: {
get: function() {
return _findElementSibling(this, true);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vPrevious
* @type vnode
* @since 0.0.1
*/
vPrevious: {
get: function() {
return _findNodeSibling(this);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vPreviousElement
* @type vnode
* @since 0.0.1
*/
vPreviousElement: {
get: function() {
return _findElementSibling(this);
}
}
});
return vNodeProto;
};
},{"./attribute-extractor.js":2846,"./html-parser.js":2850,"./vdom-ns.js":2852,"js-ext/extra/hashmap.js":2804,"js-ext/extra/lightmap.js":2805,"js-ext/lib/array.js":2806,"js-ext/lib/object.js":2807,"js-ext/lib/string.js":2809,"polyfill":2826,"utils/lib/timers.js":2829}],2854:[function(require,module,exports){
arguments[4][100][0].apply(exports,arguments)
},{"./partials/extend-document.js":2848,"./partials/extend-element.js":2849,"./partials/node-parser.js":2851,"js-ext/extra/hashmap.js":2804,"js-ext/lib/object.js":2807,"utils/lib/timers.js":2829}],2855:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2856,"./lib/timers.js":2857}],2856:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3239,"polyfill/polyfill-base.js":2860}],2857:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2860}],2858:[function(require,module,exports){
module.exports=require(14)
},{}],2859:[function(require,module,exports){
module.exports=require(15)
},{}],2860:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2858,"./lib/window.console.js":2859}],2861:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":2866,"js-ext/lib/object.js":2867,"polyfill/polyfill-base.js":2879}],2862:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":2861}],2863:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":2861,"js-ext/extra/classes.js":2865,"js-ext/lib/object.js":2867}],2864:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":2861,"./event-emitter.js":2862,"./event-listener.js":2863}],2865:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":2867,"js-ext/extra/hashmap.js":2866,"polyfill/polyfill-base.js":2870}],2866:[function(require,module,exports){
module.exports=require(4)
},{}],2867:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2866,"polyfill/polyfill-base.js":2870,"utils":2871}],2868:[function(require,module,exports){
module.exports=require(14)
},{}],2869:[function(require,module,exports){
module.exports=require(15)
},{}],2870:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2868,"./lib/window.console.js":2869}],2871:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2872,"./lib/timers.js":2873}],2872:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2866,"polyfill/polyfill-base.js":2876}],2873:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2876}],2874:[function(require,module,exports){
module.exports=require(14)
},{}],2875:[function(require,module,exports){
module.exports=require(15)
},{}],2876:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2874,"./lib/window.console.js":2875}],2877:[function(require,module,exports){
module.exports=require(14)
},{}],2878:[function(require,module,exports){
module.exports=require(15)
},{}],2879:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2877,"./lib/window.console.js":2878}],2880:[function(require,module,exports){
module.exports=require(737)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],2881:[function(require,module,exports){
arguments[4][738][0].apply(exports,arguments)
},{"./css/focusmanager.css":2880,"event-mobile":2882,"js-ext/extra/hashmap.js":2985,"js-ext/lib/object.js":2986,"node-plugin":2996,"polyfill":3168,"utils":3169,"window-ext":3175}],2882:[function(require,module,exports){
arguments[4][634][0].apply(exports,arguments)
},{"./lib/hammer-2.0.4.js":2883,"event-dom":2884}],2883:[function(require,module,exports){
module.exports=require(635)
},{"utils":2979}],2884:[function(require,module,exports){
"use strict";
/**
* Integrates DOM-events to event. more about DOM-events:
* http://www.smashingmagazine.com/2013/11/12/an-introduction-to-dom-events/
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @example
* Event = require('event-dom')(window);
*
* @module event
* @submodule event-dom
* @class Event
* @since 0.0.1
*/
var NAME = '[event-dom]: ',
Event = require('event'),
later = require('utils').later,
createHashMap = require('js-ext/extra/hashmap.js').createMap,
OUTSIDE = 'outside',
REGEXP_NODE_ID = /^#\S+$/,
REGEXP_EXTRACT_NODE_ID = /#(\S+)/,
REGEXP_UI_OUTSIDE = /^.+outside$/,
TIME_BTN_PRESSED = 200,
PURE_BUTTON_ACTIVE = 'pure-button-active',
UI = 'UI:',
NODE = 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
TAP = 'tap',
CLICK = 'click',
RIGHTCLICK = 'right'+CLICK,
CENTERCLICK = 'center'+CLICK,
EV_REMOVED = UI+NODE+REMOVE,
EV_INSERTED = UI+NODE+INSERT,
EV_CONTENT_CHANGE = UI+NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = UI+ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = UI+ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = UI+ATTRIBUTE+INSERT,
mutationEventsDefined = false,
NO_DEEP_SEARCH = {},
/*
* Internal hash containing all DOM-events that are listened for (at `document`).
*
* DOMEvents = {
* 'tap': callbackFn,
* 'mousemove': callbackFn,
* 'keypress': callbackFn
* }
*
* @property DOMEvents
* @default {}
* @type Object
* @private
* @since 0.0.1
*/
DOMEvents = {};
require('js-ext/lib/string.js');
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('polyfill/polyfill-base.js');
module.exports = function (window) {
var DOCUMENT = window.document,
_domSelToFunc, _evCallback, _findCurrentTargets, _preProcessor, _setupEvents, _setupMutationListener, _teardownMutationListener,
_setupDomListener, _teardownDomListener, SORT, _sortFunc, _sortFuncReversed, _getSubscribers, _selToFunc, MUTATION_EVENTS, preventClick;
require('vdom')(window);
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.EventDom) {
return Event; // Event was already extended
}
console.warn('event-dom 11');
MUTATION_EVENTS = [EV_REMOVED, EV_INSERTED, EV_CONTENT_CHANGE, EV_ATTRIBUTE_REMOVED, , EV_ATTRIBUTE_CHANGED, EV_ATTRIBUTE_INSERTED];
/*
* Transforms the selector to a valid function
*
* @method _selToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_selToFunc = function(customEvent, subscriber) {
Event._sellist.some(function(selFn) {
return selFn(customEvent, subscriber);
});
};
/*
* Creates a filterfunction out of a css-selector. To be used for catching any dom-element, without restrictions
* of any context (like Parcels can --> Parcel.Event uses _parcelSelToDom instead)
* On "non-outside" events, subscriber.t is set to the node that first matches the selector
* so it can be used to set as e.target in the final subscriber
*
* @method _domSelToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_domSelToFunc = function(customEvent, subscriber) {
// this stage is runned during subscription
var outsideEvent = REGEXP_UI_OUTSIDE.test(customEvent),
selector = subscriber.f,
context = subscriber.o,
vnode = subscriber.o.vnode,
isCustomElement = vnode && vnode.isItag,
visibleContent = isCustomElement && !vnode.domNode.contentHidden,
nodeid, byExactId, newTarget, deepSearch;
isCustomElement = false;
console.log(NAME, '_domSelToFunc type of selector = '+typeof selector);
// note: selector could still be a function: in case another subscriber
// already changed it.
if ((!selector && !isCustomElement) || (typeof selector === 'function')) {
subscriber.n || (subscriber.n = isCustomElement ? context : DOCUMENT);
return true;
}
selector || (selector='');
nodeid = selector.match(REGEXP_EXTRACT_NODE_ID);
nodeid ? (subscriber.nId=nodeid[1]) : (subscriber.n=isCustomElement ? context : DOCUMENT);
byExactId = REGEXP_NODE_ID.test(selector);
deepSearch = !NO_DEEP_SEARCH[customEvent];
// set the selector to `subscriber._s` so that e.currentTarget can calculate it:
subscriber._s = selector;
subscriber.f = function(e) {
// this stage is runned when the event happens
console.log(NAME, '_domSelToFunc inside filter. selector: '+selector);
var node = e.target,
vnode = node.vnode,
character1 = selector && selector.substr(1),
match = false;
if (!isCustomElement || visibleContent || context.contains(node)) {
if (selector==='') {
match = true;
}
else {
// e.target is the most deeply node in the dom-tree that caught the event
// our listener uses `selector` which might be a node higher up the tree.
// we will reset e.target to this node (if there is a match)
// note that e.currentTarget will always be `document` --> we're not interested in that
// also, we don't check for `node`, but for node.matchesSelector: the highest level `document`
// is not null, yet it doesn't have .matchesSelector so it would fail
// we DON'T want this for the focus and blur event!
if (deepSearch) {
if (vnode) {
// we go through the vdom
if (!vnode.removedFromDOM) {
while (vnode && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = vnode.domNode;
}
vnode = vnode.vParent;
}
}
}
else {
// we go through the dom
while (node.matchesSelector && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the dom');
match = byExactId ? (node.id===character1) : node.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = node;
}
node = node.parentNode;
}
}
}
else {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
if (vnode) {
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
}
else {
match = node.matchesSelector && (byExactId ? (node.id===character1) : node.matchesSelector(selector));
}
match && (e.sourceTarget=node);
}
}
}
if (outsideEvent && !match) {
// there is a match for the outside-event:
// we need to set e.sourceTarget and e.target:
newTarget = DOCUMENT.getElement(selector, true);
if (newTarget) {
e.sourceTarget = node;
subscriber.t = newTarget;
}
else {
// make return `false` because the selector is not in the dom
match = true;
}
}
console.log(NAME, '_domSelToFunc filter returns '+(!outsideEvent ? match : !match));
return !outsideEvent ? match : !match;
};
return true;
};
// at this point, we need to find out what are the current node-refs. whenever there is
// a filter that starts with `#` --> in those cases we have a bubble-chain, because the selector isn't
// set up with `document` at its root.
// we couldn't do this at time of subscribtion, for the nodes might not be there at that time.
// however, we only need to do this once: we store the value if we find them
// no problem when the nodes leave the dom later: the previous filter wouldn't pass
_findCurrentTargets = function(subscribers) {
console.log(NAME, '_findCurrentTargets');
subscribers.forEach(
function(subscriber) {
console.log(NAME, '_findCurrentTargets for single subscriber. nId: '+subscriber.nId);
subscriber.nId && (subscriber.n=DOCUMENT.getElementById(subscriber.nId, true));
}
);
};
/*
* Generates an event through our Event-system. Does the actual transportation from DOM-events
* into our Eventsystem. It also looks at the response of our Eventsystem: if our system
* halts or preventDefaults the customEvent, then the original DOM-event will be preventDefaulted.
*
* @method _evCallback
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_evCallback = function(e) {
console.log(NAME, '_evCallback');
var allSubscribers = Event._subs,
eType = e.type,
eventobject, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs, subsOutside,
subscribers, eventobjectOutside, wildcard_named_subsOutside, customEvent, eventName, which;
eventName = eType;
// first: a `click` event might be needed to transformed into `rightclick`:
if (eventName===CLICK) {
which = e.which;
(which===2) && (eventName=CENTERCLICK);
(which===3) && (eventName=RIGHTCLICK);
}
if (eventName==='tap') {
// prevent the next click-event
preventClick = true;
e.clientX || (e.clientX = e.center && e.center.x);
e.clientY || (e.clientY = e.center && e.center.y);
}
else if (preventClick && (eventName===CLICK)) {
preventClick = false;
return;
}
if (eventName===CLICK) {
eventName = 'tap';
e.center = {
x: e.clientX,
y: e.clientY
};
e.eventType = 4;
e.pointerType = 'mouse';
e.tapCount = 1;
}
customEvent = 'UI:'+eventName;
subs = allSubscribers[customEvent];
wildcard_named_subs = allSubscribers['*:'+eventName];
named_wildcard_subs = allSubscribers['UI:*'];
wildcard_wildcard_subs = allSubscribers['*:*'];
// Emit the dom-event though our eventsystem:
// NOTE: emit() needs to be synchronous! otherwise we wouldn't be able
// to preventDefault in time
//
// e = eventobject from the DOM-event OR gesture-event
// eventobject = eventobject from our Eventsystem, which get returned by calling `emit()`
// now so the work:
subscribers = _getSubscribers(e, true, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
eventobject = Event._emit(e.target, customEvent, e, subscribers, [], _preProcessor, false, true);
// now check outside subscribers
subsOutside = allSubscribers[customEvent+OUTSIDE];
wildcard_named_subsOutside = allSubscribers['*:'+eventName+OUTSIDE];
subscribers = _getSubscribers(e, true, subsOutside, wildcard_named_subsOutside);
eventobjectOutside = Event._emit(e.target, customEvent+OUTSIDE, e, subscribers, [], _preProcessor, false, true);
// if eventobject was preventdefaulted or halted: take appropriate action on
// the original dom-event. Note: only the original event can caused this, not the outsideevent
// stopPropagation on the original eventobject has no impact on our eventsystem, but who know who else is watching...
// be carefull though: not all gesture events have e.stopPropagation
eventobject.status.halted && e.stopPropagation && e.stopPropagation();
// now we might need to preventDefault the original event.
// be carefull though: not all gesture events have e.preventDefault
if ((eventobject.status.halted || eventobject.status.defaultPrevented || eventobject.status.defaultPreventedContinue) && e.preventDefault) {
e.preventDefault();
}
if (eventobject.status.ok) {
// last step: invoke the aftersubscribers
// we need to do this asynchronous: this way we pass them AFTER the DOM-event's defaultFn
// also make sure to paas-in the payload of the manipulated eventobject
subscribers = _getSubscribers(e, false, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent, eventobject, [], subscribers, _preProcessor, true), 10);
// now check outside subscribers
subscribers = _getSubscribers(e, false, subsOutside, wildcard_named_subsOutside);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent+OUTSIDE, eventobjectOutside, [], subscribers, _preProcessor, true), 10);
}
};
/*
* Creates an array of subscribers in the right order, conform their position in the DOM.
* Only subscribers that match the filter are involved.
*
* @method _getSubscribers
* @param e {Object} eventobject
* @param before {Boolean} whether it is a before- or after-subscriber
* @param subs {Array} array with subscribers
* @param wildcard_named_subs {Array} array with subscribers
* @param named_wildcard_subs {Array} array with subscribers
* @param wildcard_wildcard_subs {Array} array with subscribers
* @private
* @since 0.0.1
*/
_getSubscribers = function(e, before, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs) {
var subscribers = [],
beforeOrAfter = before ? 'b' : 'a',
saveConcat = function(extrasubs) {
extrasubs && extrasubs[beforeOrAfter] && (subscribers=subscribers.concat(extrasubs[beforeOrAfter]));
};
saveConcat(subs);
saveConcat(wildcard_named_subs);
saveConcat(named_wildcard_subs);
saveConcat(wildcard_wildcard_subs);
if (subscribers.length>0) {
subscribers = function(array, testFunc) {
// quickest way to filter an array: see http://jsperf.com/array-filter-performance/4
var filtered = array.slice(0), i;
for (i=array.length-1; i>=0; i--) {
console.log(NAME, 'filtercheck for subscriber');
testFunc(array[i]) || filtered.splice(i, 1);
}
return filtered;
}(subscribers, function(subscriber) {return (!subscriber.f || subscriber.f.call(subscriber.o, e));});
if (subscribers.length>0) {
// _findCurrentTargets(subscribers);
// sorting, based upon the sortFn
subscribers.sort(SORT);
}
}
return subscribers;
};
/*
* Sets e.target and e.sourceTarget for the single subscriber.
* Needs to be done for evenry single subscriber, because with a single event, these values change for each subscriber
*
* @method _preProcessor
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_preProcessor = function(subscriber, e) {
console.log(NAME, '_preProcessor');
// inside the aftersubscribers, we may need exit right away.
// this would be the case whenever stopPropagation or stopImmediatePropagation was called
// in case the subscribernode equals the node on which stopImmediatePropagation was called: return true
var propagationStopped, immediatePropagationStopped,
targetnode = (subscriber.t || subscriber.n);
immediatePropagationStopped = e.status.immediatePropagationStopped;
if (immediatePropagationStopped && ((immediatePropagationStopped===targetnode) || !immediatePropagationStopped.contains(targetnode))) {
console.log(NAME, '_preProcessor will return true because of immediatePropagationStopped');
return true;
}
// in case the subscribernode does not fall within or equals the node on which stopPropagation was called: return true
propagationStopped = e.status.propagationStopped;
if (propagationStopped && (propagationStopped!==targetnode) && !propagationStopped.contains(targetnode)) {
console.log(NAME, '_preProcessor will return true because of propagationStopped');
return true;
}
e._s = subscriber._s;
// now we might need to set e.target to the right node:
// the filterfunction might have found the true domnode that should act as e.target
// and set it at subscriber.t
// also, we need to backup the original e.target: this one should be reset when
// we encounter a subscriber with its own filterfunction instead of selector
if (subscriber.t) {
e.sourceTarget || (e.sourceTarget=e.target);
e.target = subscriber.t;
}
else {
e.sourceTarget && (e.target=e.sourceTarget);
}
return false;
};
/*
* Transports DOM-events to the Event-system. Catches events at their most early stage:
* their capture-phase. When these events happen, a new customEvent is generated by our own
* Eventsystem, by calling _evCallback(). This way we keep DOM-events and our Eventsystem completely separated.
*
* @method _setupDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_setupDomListener = function(customEvent, subscriber) {
console.log(NAME, '_setupDomListener');
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0],
eventName = eventSplitted[1],
outsideEvent = REGEXP_UI_OUTSIDE.test(eventName);
// be careful: anyone could also register an `outside`-event.
// in those cases, the DOM-listener must be set up without `outside`
outsideEvent && (eventName=eventName.substring(0, eventName.length-7));
// if eventName equals `mouseover` or `mouseleave` then we quit:
// people should use `mouseover` and `mouseout`
if ((eventName==='mouseenter') || (eventName==='mouseleave')) {
console.warn(NAME, 'Subscription to '+eventName+' not supported, use mouseover and mouseout: this eventsystem uses these non-noisy so they act as mouseenter and mouseleave');
return;
}
// only accept tap-events, yet later on we WILL need to listen for click-events
(eventName===CLICK) && (eventName=TAP);
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(emitterName+':'+eventName+(outsideEvent ? OUTSIDE : ''), subscriber);
// already registered? then return, also return if someone registered for UI:*
if (DOMEvents[eventName] || (eventName==='*')) {
// cautious: one might have registered the event, but not yet the outsideevent.
// in that case: save this setting:
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
return;
}
DOMEvents[eventName] = true;
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
// one exception: windowresize should listen to the window-object
if (eventName==='resize') {
window.addEventListener(eventName, _evCallback);
}
else {
((eventName===RIGHTCLICK) || (eventName===CENTERCLICK)) && (eventName=TAP);
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.addEventListener(eventName, _evCallback, true);
// listen for both `tap` and `click` events to happen
(eventName===TAP) && DOCUMENT.addEventListener(CLICK, _evCallback, true);
}
};
_setupEvents = function() {
var lastFocussed;
// make sure disabled buttons don't work:
Event.before(['tap', 'press'], function(e) {
e.preventDefault();
}, '.pure-button-disabled, button[disabled]');
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.before(
'keydown',
function(e) {
e._buttonPressed = true;
Event.emit(e.target, 'UI:tap', e);
},
function(e) {
var keyCode = e.keyCode;
return (e.target.getTagName()==='BUTTON') && ((keyCode===13) || (keyCode===32));
}
);
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.after(
'tap',
function(e) {
var buttonNode = e.target;
if (e._buttonPressed) {
buttonNode.setClass(PURE_BUTTON_ACTIVE);
e._noRender = true;
// even if the node isn't in the DOM, we can still try to manipulate it:
// the vdom makes sure no errors occur when the node is already removed
later(buttonNode.removeClass.bind(buttonNode, PURE_BUTTON_ACTIVE), TIME_BTN_PRESSED);
}
}
);
// fix activeElement on Mac
Event.before('focus', function(e) {
// will come here more often because of bubblechain
// however, the last pass-through will set the deepest node
lastFocussed = e.target;
});
// patching DOCUMENT.activeElement because it doesn't work well in a Mac: https://developer.mozilla.org/en-US/docs/Web/API/document.activeElement
// DOCUMENT._activeElement is used with the patch for DOCUMENT.activeElement its getter
Event.after('focus', function() {
DOCUMENT._activeElement = lastFocussed;
});
Event.before('blur', function() {
DOCUMENT._activeElement = null;
});
// Note: window.document has no prototype
Object.defineProperty(DOCUMENT, 'activeElement', {
get: function() {
return DOCUMENT._activeElement || DOCUMENT.body;
}
});
};
_setupMutationListener = function() {
DOCUMENT.hasMutationSubs = true;
if (!mutationEventsDefined) {
Event.defineEvent(EV_REMOVED).unPreventable();
Event.defineEvent(EV_INSERTED).unPreventable();
Event.defineEvent(EV_CONTENT_CHANGE).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_REMOVED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_CHANGED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_INSERTED).unPreventable();
mutationEventsDefined = true;
}
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFunc = function(subscriberOne, subscriberTwo) {
return (subscriberTwo.t || subscriberTwo.n).contains(subscriberOne.t || subscriberOne.n) ? -1 : 1;
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFuncReversed = function(subscriberOne, subscriberTwo) {
return (subscriberOne.t || subscriberOne.n).contains(subscriberTwo.t || subscriberTwo.n) ? 1 : -1;
};
/*
* Removes DOM-eventsubscribers from document when they are no longer needed.
*
* @method _teardownDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @private
* @since 0.0.2
*/
_teardownDomListener = function(customEvent) {
var customEventWithoutOutside = customEvent.endsWith(OUTSIDE) ? customEvent.substr(0, customEvent.length-7) : customEvent,
eventSplitted = customEventWithoutOutside.split(':'),
eventName = eventSplitted[1],
stillInUse;
if ((customEventWithoutOutside===CLICK) || (customEventWithoutOutside===RIGHTCLICK) || (customEventWithoutOutside===CENTERCLICK)) {
stillInUse = Event._subs[CLICK] ||
Event._subs[CLICK+OUTSIDE];
Event._subs[RIGHTCLICK] ||
Event._subs[RIGHTCLICK+OUTSIDE],
Event._subs[CENTERCLICK] ||
Event._subs[CENTERCLICK+OUTSIDE];
eventName = CLICK;
}
else {
stillInUse = Event._subs[customEventWithoutOutside] || Event._subs[customEventWithoutOutside+OUTSIDE];
}
if (!stillInUse) {
console.log(NAME, '_teardownDomListener '+customEvent);
// remove eventlistener from `document`
// one exeption: windowresize should listen to the window-object
if (eventName==='resize') {
window.removeEventListener(eventName, _evCallback);
}
else {
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.removeEventListener(eventName, _evCallback, true);
}
delete DOMEvents[eventName];
}
};
_teardownMutationListener = function() {
if (!Event._subs[EV_REMOVED] &&
!Event._subs[EV_INSERTED] &&
!Event._subs[EV_CONTENT_CHANGE] &&
!Event._subs[EV_ATTRIBUTE_REMOVED] &&
!Event._subs[EV_ATTRIBUTE_CHANGED] &&
!Event._subs[EV_ATTRIBUTE_INSERTED]
) {
DOCUMENT.hasMutationSubs = false;
}
};
// Now a very tricky one:
// Some browsers do an array.sort down-top instead of top-down.
// In those cases we need another sortFn, for the position on an equal match should fall
// behind instead of before (which is the case on top-down sort)
[1,2].sort(function(a /*, b */) {
SORT || (SORT=(a===2) ? _sortFuncReversed : _sortFunc);
});
// Now we do some initialization in order to make DOM-events work:
Object.defineProperty(Event._defaultEventObj, 'currentTarget', {
get: function() {
var ev = this,
e_target = ev.target;
if (e_target && ev._s) {
if (e_target.matches(ev._s)) {
return e_target;
}
return e_target.inside(ev._s) || undefined;
}
}
});
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(UI+'*', _setupDomListener, Event)
._setEventObjProperty('stopPropagation', function() {this.status.ok || (this.status.propagationStopped = this.target);})
._setEventObjProperty('stopImmediatePropagation', function() {this.status.ok || (this.status.immediatePropagationStopped = this.target);});
// Notify when someone subscribes to any event at all --> we might need to transform the filterFn from a selector into a true fnction
// this is already done automaticly by _setupDomListener fo UI:* events
Event.notify('*:*', function(customEvent, subscriber) {
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0];
if ((emitterName!=='UI') && (typeof subscriber.f==='string')) {
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(customEvent, subscriber);
}
}, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(UI+'*', _teardownDomListener, Event);
Event._sellist = [_domSelToFunc];
_setupEvents();
// making HTMLElement to be able to emit using event-emitter:
(function(HTMLElementPrototype) {
HTMLElementPrototype.merge(Event.Emitter('UI'));
}(window.HTMLElement.prototype));
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(MUTATION_EVENTS, _setupMutationListener, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(MUTATION_EVENTS, _teardownMutationListener, Event);
// Note: window.document has no prototype
DOCUMENT.suppressMutationEvents = function(suppress) {
this._suppressMutationEvents = suppress;
};
// Event.noDeepDomEvt and Event._domCallback are the only method that is added to Event.
// We need to do this, because `event-mobile` needs access to the same method.
// We could have done without this method and instead listen for a custom-event to handle
// Mobile events, however, that would lead into 2 eventcycli which isn't performant.
/**
*
* @method noDeepDomEvt
* @param domEvent {String} the eventName that should be processed without deepsearch
* @param e {Object} eventobject
* @for Event
* @chainable
* @since 0.0.1
*/
Event.noDeepDomEvt = function(domEvent) {
domEvent.contains(':') || (domEvent=UI+domEvent);
NO_DEEP_SEARCH[domEvent] = true;
return this;
};
/**
* Does the actual transportation from DOM-events into the Eventsystem. It also looks at the response of
* the Eventsystem: on e.halt() or e.preventDefault(), the original DOM-event will be preventDefaulted.
*
* @method _domCallback
* @param eventName {String} the customEvent that is transported to the eventsystem
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
Event._domCallback = function(e) {
_evCallback(e);
};
// store module:
window._ITSAmodules.EventDom = Event;
return Event;
};
},{"event":2888,"js-ext/extra/hashmap.js":2904,"js-ext/lib/array.js":2905,"js-ext/lib/object.js":2906,"js-ext/lib/string.js":2907,"polyfill/polyfill-base.js":2919,"utils":2920,"vdom":2978}],2885:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":2890,"js-ext/lib/object.js":2891,"polyfill/polyfill-base.js":2903}],2886:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":2885}],2887:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":2885,"js-ext/extra/classes.js":2889,"js-ext/lib/object.js":2891}],2888:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":2885,"./event-emitter.js":2886,"./event-listener.js":2887}],2889:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":2891,"js-ext/extra/hashmap.js":2890,"polyfill/polyfill-base.js":2894}],2890:[function(require,module,exports){
module.exports=require(4)
},{}],2891:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2890,"polyfill/polyfill-base.js":2894,"utils":2895}],2892:[function(require,module,exports){
module.exports=require(14)
},{}],2893:[function(require,module,exports){
module.exports=require(15)
},{}],2894:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2892,"./lib/window.console.js":2893}],2895:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2896,"./lib/timers.js":2897}],2896:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2890,"polyfill/polyfill-base.js":2900}],2897:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2900}],2898:[function(require,module,exports){
module.exports=require(14)
},{}],2899:[function(require,module,exports){
module.exports=require(15)
},{}],2900:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2898,"./lib/window.console.js":2899}],2901:[function(require,module,exports){
module.exports=require(14)
},{}],2902:[function(require,module,exports){
module.exports=require(15)
},{}],2903:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2901,"./lib/window.console.js":2902}],2904:[function(require,module,exports){
module.exports=require(4)
},{}],2905:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":2910}],2906:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2904,"polyfill/polyfill-base.js":2910,"utils":2911}],2907:[function(require,module,exports){
module.exports=require(29)
},{}],2908:[function(require,module,exports){
module.exports=require(14)
},{}],2909:[function(require,module,exports){
module.exports=require(15)
},{}],2910:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2908,"./lib/window.console.js":2909}],2911:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2912,"./lib/timers.js":2913}],2912:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2904,"polyfill/polyfill-base.js":2916}],2913:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2916}],2914:[function(require,module,exports){
module.exports=require(14)
},{}],2915:[function(require,module,exports){
module.exports=require(15)
},{}],2916:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2914,"./lib/window.console.js":2915}],2917:[function(require,module,exports){
module.exports=require(14)
},{}],2918:[function(require,module,exports){
module.exports=require(15)
},{}],2919:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2917,"./lib/window.console.js":2918}],2920:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2921,"./lib/timers.js":2922}],2921:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2904,"polyfill/polyfill-base.js":2925}],2922:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2925}],2923:[function(require,module,exports){
module.exports=require(14)
},{}],2924:[function(require,module,exports){
module.exports=require(15)
},{}],2925:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2923,"./lib/window.console.js":2924}],2926:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],2927:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":2931,"js-ext/extra/hashmap.js":2928,"polyfill/polyfill-base.js":2937}],2928:[function(require,module,exports){
module.exports=require(4)
},{}],2929:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":2930,"../lib/object.js":2931,"./classes.js":2927,"js-ext/extra/hashmap.js":2928,"polyfill/lib/weakmap.js":2935}],2930:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":2937}],2931:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2928,"polyfill/polyfill-base.js":2937,"utils":2938}],2932:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":2937}],2933:[function(require,module,exports){
module.exports=require(29)
},{}],2934:[function(require,module,exports){
module.exports=require(14)
},{}],2935:[function(require,module,exports){
module.exports=require(57)
},{}],2936:[function(require,module,exports){
module.exports=require(15)
},{}],2937:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2934,"./lib/window.console.js":2936}],2938:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2939,"./lib/timers.js":2940}],2939:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2928,"polyfill/polyfill-base.js":2943}],2940:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2943}],2941:[function(require,module,exports){
module.exports=require(14)
},{}],2942:[function(require,module,exports){
module.exports=require(15)
},{}],2943:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2941,"./lib/window.console.js":2942}],2944:[function(require,module,exports){
module.exports=require(66)
},{}],2945:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":2944}],2946:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":2944}],2947:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":2944}],2948:[function(require,module,exports){
module.exports=require(14)
},{}],2949:[function(require,module,exports){
module.exports=require(15)
},{}],2950:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2948,"./lib/window.console.js":2949}],2951:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2952,"./lib/timers.js":2953}],2952:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2928,"polyfill/polyfill-base.js":2956}],2953:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2956}],2954:[function(require,module,exports){
module.exports=require(14)
},{}],2955:[function(require,module,exports){
module.exports=require(15)
},{}],2956:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2954,"./lib/window.console.js":2955}],2957:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":2958}],2958:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":2959,"js-ext/lib/object.js":2960}],2959:[function(require,module,exports){
module.exports=require(4)
},{}],2960:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2959,"polyfill/polyfill-base.js":2963,"utils":2964}],2961:[function(require,module,exports){
module.exports=require(14)
},{}],2962:[function(require,module,exports){
module.exports=require(15)
},{}],2963:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2961,"./lib/window.console.js":2962}],2964:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2965,"./lib/timers.js":2966}],2965:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2959,"polyfill/polyfill-base.js":2969}],2966:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2969}],2967:[function(require,module,exports){
module.exports=require(14)
},{}],2968:[function(require,module,exports){
module.exports=require(15)
},{}],2969:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2967,"./lib/window.console.js":2968}],2970:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":2928,"js-ext/lib/object.js":2931,"js-ext/lib/string.js":2933,"polyfill":2950,"polyfill/extra/transition.js":2945,"polyfill/extra/vendorCSS.js":2947}],2971:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":2928,"js-ext/lib/object.js":2931,"polyfill":2950}],2972:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":2928,"js-ext/lib/object.js":2931,"js-ext/lib/string.js":2933,"polyfill":2950}],2973:[function(require,module,exports){
arguments[4][95][0].apply(exports,arguments)
},{"../css/element.css":2926,"./attribute-extractor.js":2970,"./element-array.js":2971,"./html-parser.js":2974,"./node-parser.js":2975,"./vdom-ns.js":2976,"./vnode.js":2977,"js-ext/extra/hashmap.js":2928,"js-ext/lib/object.js":2931,"js-ext/lib/promise.js":2932,"js-ext/lib/string.js":2933,"polyfill":2950,"polyfill/extra/transition.js":2945,"polyfill/extra/transitionend.js":2946,"polyfill/extra/vendorCSS.js":2947,"utils":2951,"window-ext":2957}],2974:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":2970,"./vdom-ns.js":2976,"js-ext/extra/hashmap.js":2928,"js-ext/lib/object.js":2931,"polyfill":2950}],2975:[function(require,module,exports){
arguments[4][97][0].apply(exports,arguments)
},{"./attribute-extractor.js":2970,"./vdom-ns.js":2976,"./vnode.js":2977,"js-ext/extra/hashmap.js":2928,"js-ext/lib/object.js":2931,"polyfill":2950}],2976:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":2928,"js-ext/lib/object.js":2931,"polyfill":2950}],2977:[function(require,module,exports){
"use strict";
/**
* Delivers the `vnode` prototype object, which is a virtualisation of an `Element` inside the Dom.
* These Elements work smoothless with the vdom (see ...).
*
* vnodes are much quicker to access and walk through than native dom-nodes. However, this is a module you don't need
* by itself: `Element`-types use these features under the hood.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* <br>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
*
* @module vdom
* @submodule vnode
* @class vnode
* @since 0.0.1
*/
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('js-ext/lib/string.js');
require('polyfill');
var createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.VNode) {
return window._ITSAmodules.VNode; // VNODE was already created
}
console.warn('VDOM 25');
var NS = require('./vdom-ns.js')(window),
extractor = require('./attribute-extractor.js')(window),
DOCUMENT = window.document,
LightMap = require('js-ext/extra/lightmap.js'),
MUTATION_EVENTS = new LightMap(),
BATCH_WILL_RUN = false,
xmlNS = NS.xmlNS,
nodeids = NS.nodeids,
htmlToVNodes = require('./html-parser.js')(window),
timers = require('utils/lib/timers.js'),
async = timers.async,
later = timers.later,
/*jshint proto:true */
PROTO_SUPPORTED = !!Object.__proto__,
/*jshint proto:false */
// cleanup memory after 1 minute: removed nodes SHOULD NOT be accessed afterwards
// because vnode would be recalculated and might be different from before
DESTROY_DELAY = 60000,
unescapeEntities = NS.UnescapeEntities,
NTH_CHILD_REGEXP = /^(?:(\d*)[n|N])([\+|\-](\d+))?$/, // an+b
STRING = 'string',
CLASS = 'class',
STYLE = 'style',
ID = 'id',
NODE= 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
EV_REMOVED = NODE+REMOVE,
EV_INSERTED = NODE+INSERT,
EV_CONTENT_CHANGE = NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = ATTRIBUTE+INSERT,
SPLIT_CHARACTER = createHashMap({
' ': true,
'>': true,
'+': true, // only select the element when it is immediately preceded by the former element
'~': true // only the element when it has the former element as a sibling. (just like `+`, but less strict)
}),
STORABLE_SPLIT_CHARACTER = createHashMap({
'>': true,
'+': true,
'~': true
}),
SIBLING_MATCH_CHARACTER = createHashMap({
'+': true,
'~': true
}),
ATTR_DETAIL_SPECIFIERS = createHashMap({
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to attribute-name end-tokens.
*
* @property END_ATTRIBUTENAME
* @default {
* '=': true,
* ']': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
END_ATTRIBUTENAME = createHashMap({
'=': true,
']': true,
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to different changes of Element nodeType changes.
*
* @property NODESWITCH
* @default {
* 1: {
* 1: 1,
* 3: 2,
* 8: 3
* },
* 3: {
* 1: 4,
* 3: 5,
* 8: 6
* },
* 8: {
* 1: 7,
* 3: 8,
* 8: 9
* }
* }
* @type Object
* @protected
* @since 0.0.1
*/
NODESWITCH = createHashMap({
1: createHashMap({
1: 1, // oldNodeType==Element, newNodeType==Element
3: 2, // oldNodeType==Element, newNodeType==TextNode
8: 3 // oldNodeType==Element, newNodeType==Comment
}),
3: createHashMap({
1: 4, // oldNodeType==TextNode, newNodeType==Element
3: 5, // oldNodeType==TextNode, newNodeType==TextNode
8: 6 // oldNodeType==TextNode, newNodeType==Comment
}),
8: createHashMap({
1: 7, // oldNodeType==Comment, newNodeType==Element
3: 8, // oldNodeType==Comment, newNodeType==TextNode
8: 9 // oldNodeType==Comment, newNodeType==Comment
})
}),
/**
* Object to gain quick access to selector start-tokens.
*
* @property SELECTOR_IDENTIFIERS
* @default {
* '#': 1,
* '.': 2,
* '[': 3
* }
* @type Object
* @protected
* @since 0.0.1
*/
SELECTOR_IDENTIFIERS = createHashMap({
'#': 1,
'.': 2,
'[': 3,
':': 4
}),
PSEUDO_FIRST_CHILD = ':first-child',
PSEUDO_FIRST_OF_TYPE = ':first-of-type',
PSEUDO_LAST_CHILD = ':last-child',
PSEUDO_LAST_OF_TYPE = ':last-of-type',
PSEUDO_NTH_CHILD = ':nth-child',
PSEUDO_NTH_LAST_CHILD = ':nth-last-child',
PSEUDO_NTH_LAST_OF_TYPE = ':nth-last-of-type',
PSEUDO_NTH_OF_TYPE = ':nth-of-type',
PSEUDO_ONLY_OF_TYPE = ':only-of-type',
PSEUDO_ONLY_CHILD = ':only-child',
/**
* Object to gain quick access to the selectors that required children
*
* @property PSEUDO_REQUIRED_CHILDREN
* @default {
* ':first-child': true,
* ':first-of-type': true,
* ':last-child': true,
* ':last-of-type': true,
* ':nth-child': true,
* ':nth-last-child': true,
* ':nth-last-of-type': true,
* ':nth-of-type': true,
* ':only-of-type': true,
* ':only-child': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
PSEUDO_REQUIRED_CHILDREN = createHashMap(),
_matchesSelectorItem, _matchesOneSelector, _findElementSibling, vNodeProto, _markRemoved, _tryReplaceChild,
_splitSelector, _findNodeSibling, _matchNthChild, _batchEmit, _emitDestroyChildren, _tryRemoveDomNode;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_CHILD] = true;
/**
* Searches for the next -or previous- node-sibling (nodeType of 1, 3 or 8).
*
* @method _findNodeSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findNodeSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
index = vParent.vChildNodes.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildNodes[index];
};
/**
* Searches for the next -or previous- Element-sibling (nodeType of 1).
*
* @method _findElementSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findElementSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
if (vnode.nodeType===1) {
index = vParent.vChildren.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildren[index];
}
else {
/*jshint noempty:true */
while ((vnode=_findNodeSibling(vnode, next)) && (vnode.nodeType!==1)) {}
/*jshint noempty:false */
return vnode;
}
};
/**
* Check whether the vnode matches a "nth-child" test, which is used for css pseudoselectors like `nth-child`, `nth-of-type` etc.
*
* @method _matchNthChild
* @param pseudoArg {String} the argument for nth-child
* @param index {Number} the index of the inspected vnode
* @return {Boolean} whether the vnode matches the nthChild test
* @protected
* @private
* @since 0.0.1
*/
_matchNthChild = function(pseudoArg, index) {
var match, k, a, b, nodeOk, nthIndex, sign, isNumber;
(pseudoArg==='even') && (pseudoArg='2n');
(pseudoArg==='odd') && (pseudoArg='2n+1');
match = pseudoArg.match(NTH_CHILD_REGEXP) || (isNumber=pseudoArg.validateNumber());
if (!match) {
return false;
}
// pseudoArg follows the pattern: `an+b`
if (!isNumber) {
a = match[1];
sign = match[2];
b = match[3] || 0;
(b==='') && (b=0);
}
else {
b = pseudoArg;
}
sign && (sign=sign[0]);
if (!a) {
// only fixed index to match
return (sign==='-') ? false : (parseInt(b, 10)===index);
}
else {
// we need to iterate
nodeOk = false;
b = window.Number(b);
for (k=0; !nodeOk; k++) {
nthIndex = (sign==='-') ? (a*k) - b : (a*k) + b;
if (nthIndex===index) {
nodeOk = true;
}
else if (nthIndex>index) {
// beyond index --> will never become a fix anymore
return false;
}
}
return nodeOk;
}
};
/**
* Check whether the vnode matches the css-selector. the css-selector should be a single selector,
* not multiple, so it shouldn't contain a `comma`.
*
* @method _matchesOneSelector
* @param vnode {vnode} the vnode to inspect
* @param selector {String} the selector-item to check the match for
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches the css-selector
* @protected
* @private
* @since 0.0.1
*/
_matchesOneSelector = function(vnode, selector, relatedVNode) {
var selList = _splitSelector(selector),
size = selList.length,
selMatch = false,
i, selectorItem, last,
rightvnode, relationMatch, checkRelation;
if (STORABLE_SPLIT_CHARACTER[selList[size-1]]) {
return false;
}
relationMatch = function(leftVNode, rightVNode, relationCharacter) {
var match, vParent, vChildren;
// when `selector` starts with `>`, `~` or `+`, then
// there should also be a match comparing a related node!
switch (relationCharacter) {
case '>':
leftVNode || (leftVNode=rightVNode.vParent);
match = (leftVNode.vChildren.indexOf(rightVNode)!==-1);
break;
case '~':
leftVNode || (leftVNode=rightVNode.vFirstElement);
vParent = leftVNode.vParent;
vChildren = vParent && vParent.vChildren;
match = vParent && (vChildren.indexOf(leftVNode)<vChildren.indexOf(rightVNode));
break;
case '+':
leftVNode || (leftVNode=rightVNode.vPreviousElement);
match = (leftVNode.vNextElement === rightVNode);
}
return !!match;
};
last = size - 1;
i = last;
while (vnode && ((selMatch || (i===last)) && (i>=0))) {
selectorItem = selList[i];
if (STORABLE_SPLIT_CHARACTER[selectorItem]) {
checkRelation = selectorItem;
i--;
}
else {
selMatch = _matchesSelectorItem(vnode, selectorItem);
if (!selMatch && (i===last)) {
return false;
}
while (!selMatch && vnode && (i!==last)) {
// may also look at nodes higher in the chain
vnode = SIBLING_MATCH_CHARACTER[selList[i+1]] ? vnode.vPreviousElement : vnode.vParent;
selMatch = vnode && _matchesSelectorItem(vnode, selectorItem);
}
selMatch && checkRelation && vnode && (selMatch=relationMatch(vnode, rightvnode, checkRelation));
rightvnode = vnode;
if (vnode) {
// do a precheck on the next checkRelation:
// and determine whether to set the vnode upper the tree or at the previous sibling:
vnode = SIBLING_MATCH_CHARACTER[selList[i-1]] ? vnode.vPreviousElement : vnode.vParent;
}
if (selMatch) {
checkRelation = false;
i--;
}
}
}
if ((rightvnode===relatedVNode) || (i!==-1)) {
selMatch = false;
}
selMatch && checkRelation && rightvnode && (selMatch=relationMatch(relatedVNode, rightvnode, checkRelation));
return selMatch;
};
/**
* Check whether the vnode matches one specific selector-item. Suppose the css-selector: "#mynode li.red .blue"
* then there are 3 selector-items: "#mynode", "li.red" and ".blue"
*
* This method also can handle the new selectors:
* <ul>
* <li>[att^=val] –-> the “begins with” selector</li>
* <li>[att$=val] –-> the “ends with” selector</li>
* <li>[att*=val] –-> the “contains” selector (might be a substring)</li>
* <li>[att~=val] –-> the “contains” selector as a separate word, separated by spaces</li>
* <li>[att|=val] –-> the “contains” selector as a separate word, separated by `|`</li>
* <li>+ --> (same level)</li>
* <li>~ --> (same level)</li>
* </ul>
*
* @method _matchesSelectorItem
* @param vnode {Object} the vnode to inspect
* @param selectorItem {String} the selector-item to check the match for
* @return {Boolean} whether the vnode matches the selector-item
* @protected
* @private
* @since 0.0.1
*/
_matchesSelectorItem = function (vnode, selectorItem) {
var i = 0,
len = selectorItem.length,
character = selectorItem[0],
tagName, id, className, attributeName, attributeValue, stringMarker, attributeisString, isBoolean, insideAttributeValue, insideAttribute,
vParent, checkBoolean, treatment, k, min, max, value, len2, index, found, pseudo, pseudoArg, arglevel, count, vParentVChildren;
if (selectorItem==='*') {
return true;
}
if (!SELECTOR_IDENTIFIERS[character]) {
// starts with tagName
tagName = '';
// reposition i to continue in the right way:
i--;
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
tagName += character;
}
if (tagName.toUpperCase()!==vnode.tag) {
return false;
}
}
while (i<len) {
switch (character) {
case '#':
id = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
id += character;
}
if (id!==vnode.id) {
return false;
}
break;
case '.':
className = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
className += character;
}
if (!vnode.hasClass(className)) {
return false;
}
break;
case '[':
attributeName = '';
while ((++i<len) && (character=selectorItem[i]) && !END_ATTRIBUTENAME[character]) {
attributeName += character;
}
// if character===']' then we have an attribute without a value-definition
if (!vnode.attrs[attributeName] || ((character===']') && (vnode.attrs[attributeName]!==''))) {
return !!vnode.attrs[attributeName];
}
// now we read the value of the attribute
// however, it could be that the selector has a special `detailed` identifier set (defined by: ATTR_DETAIL_SPECIFIERS)
if (ATTR_DETAIL_SPECIFIERS[character]) {
treatment = character; // store the character to know how the attributedata should be treaded
i++; // character should be a "=" by now
}
else {
treatment = null;
}
attributeValue = '';
stringMarker = selectorItem[i+1];
attributeisString = (stringMarker==='"') || (stringMarker==="'");
attributeisString && (i++);
// end of attributaValue = (character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker))
while ((++i<len) && (character=selectorItem[i]) && !((character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker)))) {
attributeValue += character;
}
if (attributeisString) {
// if attribute is string, then we need to _remove to last stringmarker
attributeValue = attributeValue.substr(0, attributeValue.length-1);
}
else {
// if attribute is no string, then we need to typecast its value
isBoolean = ((attributeValue.length>3) && (attributeValue.length<6) &&
(checkBoolean=attributeValue.toUpperCase()) &&
((checkBoolean==='FALSE') || (checkBoolean==='TRUE')));
// typecast the value to either Boolean or Number:
attributeValue = isBoolean ? (checkBoolean==='TRUE') : parseFloat(attributeValue);
}
// depending upon how the attributedata should be treated:
if (treatment) {
switch (treatment) {
case '^': // “begins with” selector
if (!vnode.attrs[attributeName].startsWith(attributeValue)) {
return false;
}
break;
case '$': // “ends with” selector
if (!vnode.attrs[attributeName].endsWith(attributeValue)) {
return false;
}
break;
case '*': // “contains” selector (might be a substring)
if (!vnode.attrs[attributeName].contains(attributeValue)) {
return false;
}
break;
case '~': // “contains” selector as a separate word, separated by spaces
if (!(' '+vnode.attrs[attributeName]+' ').contains(' '+attributeValue+' ')) {
return false;
}
break;
case '|': // “contains” selector as a separate word, separated by `|`
if (!('|'+vnode.attrs[attributeName]+'|').contains('|'+attributeValue+'|')) {
return false;
}
break;
}
}
else if (vnode.attrs[attributeName]!==attributeValue) {
return false;
}
// we still need to increase one position:
(++i<len) && (character=selectorItem[i]);
break;
case ':':
// we have a pseudo-selector
// first, find out which one
// because '::' is a valid start (though without any selection), we start to back the next character as well:
pseudo = ':'+selectorItem[++i];
pseudoArg = '';
vParent = vnode.vParent;
vParentVChildren = vParent && vParent.vChildren;
// pseudo-selectors might have an argument passed in, like `:nth-child(2n+1)` or `:not([type="checkbox"])` --> we
// store this argument inside `pseudoArg`
// also note that combinations are possible with `:not` --> `:not(:nth-child(2n+1))`
// also note that we cannot "just" look for a closing character when running into the usage of attributes:
// for example --> `:not([data-x="some data :)"])`
// that's why -once we are inside attribute-data- we need to continue until the attribute-data ends
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
if (character==='(') {
// starting arguments
arglevel = 1;
insideAttribute = false;
insideAttributeValue = false;
while ((++i<len) && (character=selectorItem[i]) && (arglevel>0)) {
if (!insideAttribute) {
if (character==='(') {
arglevel++;
}
else if (character===')') {
arglevel--;
}
else if (character==='[') {
insideAttribute = true;
}
}
else {
// inside attribute
if (!insideAttributeValue) {
if ((character==='"') || (character==="'")) {
insideAttributeValue = true;
stringMarker = character;
}
else if (character===']') {
insideAttribute = false;
}
}
else if ((character===stringMarker) && (selectorItem[i+1]===']')) {
insideAttributeValue = false;
}
}
(arglevel>0) && (pseudoArg+=character);
}
}
else {
pseudo += character;
}
}
// now, `pseudo` is known as well as its possible pseudoArg
if (!vParentVChildren && PSEUDO_REQUIRED_CHILDREN[pseudo]) {
return false;
}
switch (pseudo) {
case ':checked': // input:checked Selects every checked <input> element
if (!vnode.attrs.checked) {
return false;
}
break;
case ':disabled': // input:disabled Selects every disabled <input> element
if (!vnode.attrs.disabled) {
return false;
}
break;
case ':empty': // p:empty Selects every <p> element that has no children (including text nodes)
if (vnode.vChildNodes && (vnode.vChildNodes.length>0)) {
return false;
}
break;
case ':enabled': // input:enabled Selects every enabled <input> element
if (vnode.attrs.disabled) {
return false;
}
break;
case PSEUDO_FIRST_CHILD: // p:first-child Selects every <p> element that is the first child of its parent
if (vParentVChildren[0]!==vnode) {
return false;
}
break;
case PSEUDO_FIRST_OF_TYPE: // p:first-of-type Selects every <p> element that is the first <p> element of its parent
for (k=vParentVChildren.indexOf(vnode)-1; k>=0; k--) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':focus': // input:focus Selects the input element which has focus
if (vnode.domNode!==DOCUMENT.activeElement) {
return false;
}
break;
case ':in-range': // input:in-range Selects input elements with a value within a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || (value<min) || (value>max)) {
return false;
}
break;
case ':lang': // p:lang(it) Selects every <p> element with a lang attribute equal to "it" (Italian)
if (vnode.attrs.lang!==pseudoArg) {
return false;
}
break;
case PSEUDO_LAST_CHILD: // p:last-child Selects every <p> element that is the last child of its parent
if (vParentVChildren[vParentVChildren.length-1]!==vnode) {
return false;
}
break;
case PSEUDO_LAST_OF_TYPE: // p:last-of-type Selects every <p> element that is the last <p> element of its parent
len2 = vParentVChildren.length;
for (k=vParentVChildren.indexOf(vnode)+1; k<len2; k++) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':not': // :not(p) Selects every element that is not a <p> element
if (vnode.matchesSelector(pseudoArg)) {
return false;
}
break;
case PSEUDO_NTH_CHILD: // p:nth-child(2) Selects every <p> element that is the second child of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
index = vParentVChildren.indexOf(vnode)+1;
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_CHILD: // p:nth-last-child(2) Selects every <p> element that is the second child of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_OF_TYPE: // p:nth-last-of-type(2) Selects every <p> element that is the second <p> element of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
index = 0;
for (k=vParentVChildren.length-1; (k>=0) && !found; k--) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_OF_TYPE: // p:nth-of-type(2) Selects every <p> element that is the second <p> element of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
len2 = vParentVChildren.length;
index = 0;
for (k=0; (k<len2) && !found; k++) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_ONLY_OF_TYPE: // p:only-of-type Selects every <p> element that is the only <p> element of its parent
len2 = vParentVChildren.length;
count = 0;
for (k=0; (k<len2) && (count<=1); k++) {
(vParentVChildren[k].tag===vnode.tag) && count++;
}
if (count!==1) {
return false;
}
break;
case PSEUDO_ONLY_CHILD: // p:only-child Selects every <p> element that is the only child of its parent
if (vParentVChildren.length!==1) {
return false;
}
break;
case ':optional': // input:optional Selects input elements with no "required" attribute
if (vnode.attrs.required) {
return false;
}
break;
case ':out-of-range': // input:out-of-range Selects input elements with a value outside a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || ((value>=min) && (value<=max))) {
return false;
}
break;
case ':read-only': // input:read-only Selects input elements with the "readonly" attribute specified
if (!vnode.attrs.readonly) {
return false;
}
break;
case ':read-write': // input:read-write Selects input elements with the "readonly" attribute NOT specified
if (vnode.attrs.readonly) {
return false;
}
break;
case ':required': // input:required Selects input elements with the "required" attribute specified
if (!vnode.attrs.required) {
return false;
}
break;
case ':root': // Selects the document's root element
if (vnode.domNode!==DOCUMENT.documentElement) {
return false;
}
break;
}
}
}
return true;
};
/**
* Splits the selector into separate subselector-items that should match different elements through the tree.
* Special characters '>' and '+' are added as separate items in the hash.
*
* @method _splitSelector
* @param selector {String} the selector-item to check the match for
* @return {Array} splitted selectors
* @protected
* @private
* @since 0.0.1
*/
_splitSelector = function(selector) {
var list = [],
len = selector.length,
sel = '',
i, character, insideDataAttr;
for (i=0; i<len; i++) {
character = selector[i];
if (character==='[') {
sel += character;
insideDataAttr = true;
}
else if (character===']') {
sel += character;
insideDataAttr = false;
}
else if (insideDataAttr || !SPLIT_CHARACTER[character]) {
sel += character;
}
else {
// unique selectoritem is found, add it to the list
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
// in case the last character was '>', '+' or '~', we need to add it as a separate item
STORABLE_SPLIT_CHARACTER[character] && (list[list.length]=character);
}
}
// add the last item
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
return list;
};
_batchEmit = function() {
// we will exactly define the 'UI:'-event --> Itags will have another emitterName
MUTATION_EVENTS.each(function (mutationEvents, vnode) {
var domNode = vnode.domNode;
if (mutationEvents[EV_REMOVED]) {
domNode.emit('UI:'+EV_REMOVED);
}
else if (mutationEvents[EV_INSERTED]) {
domNode.emit('UI:'+EV_INSERTED);
}
else {
// contentchange and attributechanges can go hand in hand
mutationEvents.each(function(value, evt) {
domNode.emit('UI:'+evt, (evt===EV_CONTENT_CHANGE) ? null : {changed: value});
});
}
});
MUTATION_EVENTS.clear();
BATCH_WILL_RUN = false;
};
_emitDestroyChildren = function(vnode) {
var children = vnode.vChildren,
len = children.length,
i, vChild;
for (i=0; i<len; i++) {
vChild = children[i];
vChild._emit(EV_REMOVED);
}
};
_markRemoved = function(vnode) {
var vChildNodes = vnode.vChildNodes,
len, i, vChildNode;
if (vnode.nodeType===1) {
Object.protectedProp(vnode, 'removedFromDOM', true);
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && _markRemoved(vChildNode);
}
}
}
};
/**
* A safe way to remove a dom-node, even if it is not present.
* Will not throw a JS error when the "to be removed" node isn't in the dom
*
* @method _tryRemoveDomNode
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param childDomNode {DOMNode} the node that needs to be removed
* @since 0.0.1
*/
_tryRemoveDomNode = function(parentDomNode, childDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && childDomNode.isItag && childDomNode.isItag()) {
DOCUMENT._itagList.remove(childDomNode);
}
try {
parentDomNode._removeChild(childDomNode);
}
catch(err) {}
};
/**
* A safe way to replace a dom-node, even if the "to be replaced"-node it is not present.
* Will not throw a JS error when the "to be replaced" node isn't in the dom. in that case, the new node will
* be appended
*
* @method _tryReplaceChild
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param newChildDomNode {DOMNode} the node that needs to be replaced
* @param oldChildDomNode {DOMNode} the node that needs to be inserted
* @since 0.0.1
*/
_tryReplaceChild = function(parentDomNode, newChildDomNode, oldChildDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && oldChildDomNode.isItag && oldChildDomNode.isItag()) {
DOCUMENT._itagList.remove(oldChildDomNode);
}
try {
parentDomNode._replaceChild(newChildDomNode, oldChildDomNode);
}
catch(err) {
// if the childDomNode isn;t there any more - for whatever reason - the we need to append the newChildNode
parentDomNode._appendChild(newChildDomNode);
}
};
vNodeProto = window._ITSAmodules.VNode = {
/**
* Check whether the vnode's domNode is equal, or contains the specified Element.
*
* @method contains
* @return {Boolean} whether the vnode's domNode is equal, or contains the specified Element.
* @since 0.0.1
*/
contains: function(otherVNode, noProtectedSearch) {
var instance = this;
if (otherVNode && (otherVNode.destroyed || (noProtectedSearch && otherVNode._systemNode))) {
return false;
}
while (otherVNode && (otherVNode!==instance)) {
otherVNode = otherVNode.vParent;
if (otherVNode && noProtectedSearch && (otherVNode._systemNode || (otherVNode.isItag && otherVNode.domNode.contentHidden))) {
return false;
}
}
return (otherVNode===instance);
},
/**
* Empties the vnode.
*
* Syncs with the dom.
*
* @method empty
* @param [full=false] {Boolean} whether system-nodes should be removed as well
* @chainable
* @since 0.0.1
*/
empty: function(full) {
delete this._scripts;
return this._setChildNodes([], null, full);
},
/**
* Returns the first child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method firstOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the first child-vnode or null when not present
* @since 0.0.1
*/
firstOfVChildren: function(cssSelector) {
var instance = this,
found, i, len, vChildren, element;
if (!cssSelector) {
return instance.vFirstElementChild;
}
vChildren = instance.vChildren;
len = vChildren.length;
for (i=0; !found && (i<len); i++) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
return found;
},
/**
* Gets the innerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* Only valid for nodetype=1 (HTMLElements)
*
* @method getHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String|undefined} the innerHTML without the elements specified, or `undefined` when not an HTMLElement
* @since 0.0.1
*/
getHTML: function(exclude, includeSystemNodes) {
var instance = this,
html, vChildNodes, len, i, vChildNode;
if (instance.nodeType===1) {
if (exclude) {
Array.isArray(exclude) || (exclude=[exclude]);
}
html = '';
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
switch (vChildNode.nodeType) {
case 1:
(exclude && exclude.contains(vChildNode.domNode)) || (!includeSystemNodes && vChildNode._systemNode) || (html+=vChildNode.getOuterHTML(exclude, includeSystemNodes));
break;
case 3:
html += vChildNode.text.replace(/</g, '<').replace(/>/g, '>');
break;
case 8:
html += '<!--' + vChildNode.text.replace(/</g, '<').replace(/>/g, '>') + '-->';
}
}
}
return html;
},
/**
* Gets the outerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* @method getOuterHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String} the outerHTML
* @since 0.0.1
*/
getOuterHTML: function(exclude, includeSystemNodes) {
var instance = this,
html,
attrs = instance.attrs;
if (instance.nodeType===1) {
if (instance.nodeType!==1) {
return instance.textContent;
}
html = '<' + instance.tag.toLowerCase();
attrs.each(function(value, key) {
html += ' '+key+'="'+value+'"';
});
instance.isVoid && (html += '/');
html += '>';
if (!instance.isVoid) {
html += ((!includeSystemNodes && instance.isItag) ? '' : instance.getHTML(exclude, includeSystemNodes)) + '</' + instance.tag.toLowerCase() + '>';
}
}
return html;
},
/**
* Checks whether the vnode has any vChildNodes (nodeType of 1, 3 or 8).
*
* @method hasVChildNodes
* @return {Boolean} whether the vnode has any vChildNodes.
* @since 0.0.1
*/
hasVChildNodes: function() {
return this.vChildNodes ? (this.vChildNodes.length>0) : false;
},
/**
* Checks whether the vnode has any vChildren (vChildNodes with nodeType of 1).
*
* @method hasVChildren
* @return {Boolean} whether the vnode has any vChildren.
* @since 0.0.1
*/
hasVChildren: function() {
return this.vChildNodes ? (this.vChildren.length>0) : false;
},
/**
* Checks whether the className is present on the vnode.
*
* @method hasClass
* @param className {String|Array} the className to check for. May be an Array of classNames, which all needs to be present.
* @return {Boolean} whether the className (or classNames) is present on the vnode
* @since 0.0.1
*/
hasClass: function(className) {
var instance = this,
check = function(cl) {
return !!instance.classNames[cl];
};
if (!instance.classNames) {
return false;
}
if (typeof className === STRING) {
return check(className);
}
else if (Array.isArray(className)) {
return className.every(check);
}
return false;
},
/**
* Returns the last child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method lastOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the last child-vnode or null when not present
* @since 0.0.1
*/
lastOfVChildren: function(cssSelector) {
var vChildren = this.vChildren,
found, i, element;
if (vChildren) {
if (!cssSelector) {
return this.vLastElementChild;
}
for (i=vChildren.length-1; !found && (i>=0); i--) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
}
return found;
},
/**
* Checks whether the vnode matches one of the specified selectors. `selectors` can be one, or multiple css-selectors,
* separated by a `comma`. For example: "#myid li.red blue" is one selector, "div.red, div.blue, div.green" are three selectors.
*
* @method matchesSelector
* @param selectors {String} one or more css-selectors
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches one of the selectors
* @since 0.0.1
*/
matchesSelector: function(selectors, relatedVNode) {
var instance = this;
if (instance.nodeType!==1) {
return false;
}
selectors = selectors.split(',');
// we can use Array.some, because there won't be many separated selectoritems,
// so the final invocation won't be delayed much compared to looping
return selectors.some(function(selector) {
return _matchesOneSelector(instance, selector, relatedVNode);
});
},
/**
* Reloads the DOM-attribute into the vnode.
*
* @method matchesSelector
* @param attributeName {String} the name of the attribute to be reloaded.
* @return {Node} the domNode that was reloaded.
* @since 0.0.1
*/
reloadAttr: function(attributeName) {
var instance = this,
domNode = instance.domNode,
attributeValue = domNode._getAttribute(attributeName),
attrs = instance.attrs,
extractStyle, extractClass;
if (instance.nodeType===1) {
attributeValue || (attributeValue='');
if (attributeValue==='') {
delete attrs[attributeName];
// in case of STYLE attributeName --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attributeName --> special treatment
(attributeName===CLASS) && (instance.classNames={});
// in case of ID attributeName --> special treatment
if ((attributeName===ID) && (instance.id)) {
delete nodeids[instance.id];
delete instance.id;
}
}
else {
attrs[attributeName] = attributeValue;
// in case of STYLE attributeName --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(attributeValue);
attributeValue = extractStyle.attrStyle;
if (attributeValue) {
attrs.style = attributeValue;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attributeName --> special treatment
extractClass = extractor.extractClass(attributeValue);
attributeValue = extractClass.attrClass;
if (attributeValue) {
attrs[CLASS] = attributeValue;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (instance.id!==attributeValue) && (delete nodeids[instance.id]);
instance.id = attributeValue;
nodeids[attributeValue] = domNode;
}
}
}
return domNode;
},
/**
* Returns the vnode's style in a serialized form: the way it appears in the dom.
*
* @method serializeStyles
* @return {String} vnode's style
* @since 0.0.1
*/
serializeStyles: function() {
return extractor.serializeStyles(this.styles);
},
/**
* Sets the vnode's and dom-nodes inner HTML.
*
* Syncs with the dom. Can be invoked multiple times without issues.
*
* @method setHTML
* @param content {String} the innerHTML
* @param [suppressItagRender] {Boolean} to suppress Itags from rendering
* @param [allowScripts=false] {Boolean} whether scripts are allowed --> these should be defined with `xscript` instead of `script`
* @chainable
* @since 0.0.1
*/
setHTML: function(content, suppressItagRender, allowScripts) {
var instance = this;
instance._setChildNodes(htmlToVNodes(content, vNodeProto, instance.ns, null, suppressItagRender, allowScripts), suppressItagRender);
return instance;
},
/**
* Syncs the vnode's nodeid (if available) inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom. Can be invoked multiple times without issues.
*
* @method storeId
* @chainable
* @since 0.0.1
*/
storeId: function() {
// store node/vnode inside WeakMap:
var instance = this;
instance.id ? (nodeids[instance.id]=instance.domNode) : (delete nodeids[instance.id]);
return instance;
},
//---- private ------------------------------------------------------------------
_addToTaglist: function() {
var instance = this,
itagList;
if (instance.isItag) {
itagList = DOCUMENT.getItags(); // also reads the dom if the list isn't build yet
instance._data || Object.protectedProp(instance, '_data', {});
if (!instance._data.ce_destroyed && !itagList.contains(instance.domNode)) {
itagList.push(instance.domNode);
}
}
},
/**
* Adds a vnode to the end of the list of vChildNodes.
*
* Syncs with the DOM.
*
* @method _appendChild
* @param VNode {vnode} vnode to append
* @private
* @return {Node} the Node that was appended
* @since 0.0.1
*/
_appendChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
size, noProccessScript, scriptContent;
if ((VNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (VNode.attrs && VNode.attrs.src) ? VNode.attrs.src : VNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
VNode._moveToParent(instance);
instance.domNode._appendChild(domNode);
if (VNode.nodeType===3) {
size = instance.vChildNodes.length;
instance._normalize();
// if the size changed, then the domNode was merged
(size===instance.vChildNodes.length) || (domNode=instance.vChildNodes[instance.vChildNodes.length-1].domNode);
}
if (VNode.nodeType===1) {
VNode._addToTaglist();
VNode._emit(EV_INSERTED);
}
return domNode;
}
},
/**
* Removes the vnode from its parent vChildNodes- and vChildren-list.
*
* Does NOT sync with the dom.
*
* @method _deleteFromParent
* @private
* @chainable
* @since 0.0.1
*/
_deleteFromParent: function() {
var instance = this,
vParent = instance.vParent;
if (vParent && vParent.vChildNodes) {
vParent.vChildNodes.remove(instance);
// force to recalculate the vChildren on a next call:
(instance.nodeType===1) && (vParent._vChildren=null);
}
return instance;
},
/**
* Cleans up (empties) vnode's `_data`
*
* @method _cleanData
* @private
* @chainable
* @since 0.0.1
*/
_cleanData: function() {
var instance = this,
data = instance._data;
data && data.each(
function(value, key) {
delete data[key];
}
);
return instance;
},
/**
* Cleans up (removes) duplicated `style` definitions.
*
* @method _cleanupStyle
* @private
* @chainable
* @since 0.0.1
*/
_cleanupStyle: function() {
var instance = this,
compare = [],
removal = [],
vChildren = instance.vChildren,
len = vChildren.length,
vChild, i, styleContent, vChildInner;
for (i=0; i<len; i++) {
vChild = vChildren[i];
if (vChild.tag==='STYLE') {
vChildInner = vChild.vChildNodes[0];
if (vChildInner) {
styleContent = vChildInner.text;
if (compare.contains(styleContent)) {
removal[removal.length] = vChild;
}
else {
compare[compare.length] = styleContent;
}
}
else {
// empty style-tag
removal[removal.length] = vChild;
}
}
}
removal.forEach(function(vnode) {
instance._removeChild(vnode);
});
},
/**
* Destroys the vnode and all its vnode-vChildNodes.
* Removes it from its vParent.vChildNodes list,
* also removes its definitions inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom.
*
* @method _destroy
* @private
* @chainable
* @since 0.0.1
*/
_destroy: function(silent) {
var instance = this,
vChildNodes = instance.vChildNodes,
len, i, vChildNode, vParent, treeNodes;
if (!instance.destroyed) {
if (!silent) {
// Because we don't want to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
later(function() {
instance._emit(EV_REMOVED, null, null, null, true);
}, 5);
}
Object.protectedProp(instance, 'destroyed', true);
// first: determine the dom-tree, which module `event-dom` needs to determine where the node was before it was destroyed:
treeNodes = [instance];
vParent = instance.vParent;
while (vParent) {
treeNodes[treeNodes.length] = vParent;
vParent = vParent.vParent;
}
// mark all its vChildNodes so we can see if the node is in the DOM
_markRemoved(instance);
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && instance.isItag) {
DOCUMENT._itagList.remove(instance.domNode);
}
instance._scripts && (instance._scripts.length=0);
// The definite cleanup needs to be done after a timeout:
// someone might need to handle the Element when removed (fe to cleanup specific things)
later(function() {
instance._cleanData();
if (instance.nodeType===1) {
// _destroy all its vChildNodes
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && vChildNode._destroy(true);
}
}
}
instance._vChildren = null;
// explicitely set instance.domNode._vnode and instance.domNode to null in order to prevent problems with the GC (we break the circular reference)
delete instance.domNode._vnode;
// if valid id, then _remove the DOMnodeRef from internal hash
instance.id && delete nodeids[instance.id];
}, silent ? 0 : DESTROY_DELAY);
instance._deleteFromParent();
// Do not make domNode `null` --> it could be used even when not in the dom
}
return instance;
},
_emit: function(evt, attribute, newValue, prevValue, destroyEvt) {
/**
* Emitted by every Element that gets inserted.
*
* @event nodeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets removed.
*
* @event noderemove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets its content changed (innerHTML/innerText).
*
* @event nodecontentchange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute inserted.
*
* @event attributeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* </ul>
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute removed.
*
* @event attributeremove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Strings of the attributeNames that are removed
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute changed.
*
* @event attributechange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* <li>prevValue</li>
* </ul>
* @since 0.1
*/
var instance = this,
silent, attrMutations, mutationEvents, mutation, vParent;
if (!DOCUMENT.hasMutationSubs || (instance.nodeType!==1)) {
return;
}
silent = !!DOCUMENT._suppressMutationEvents;
if (!silent && (destroyEvt || !instance.destroyed)) {
// Because we don't wannt to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
mutationEvents = MUTATION_EVENTS.get(instance) || {};
if (attribute) {
attrMutations = mutationEvents[evt] || [];
if (evt===EV_ATTRIBUTE_REMOVED) {
mutation = attribute;
}
else {
mutation = {
attribute: attribute
};
if ((evt===EV_ATTRIBUTE_INSERTED) || (evt===EV_ATTRIBUTE_CHANGED)) {
mutation.newValue = newValue;
}
if ((evt===EV_ATTRIBUTE_CHANGED) && prevValue) {
mutation.prevValue = prevValue;
}
}
attrMutations.push(mutation);
mutationEvents[evt] = attrMutations;
}
else {
mutationEvents[evt] = true;
}
MUTATION_EVENTS.set(instance, mutationEvents);
// now set all parent to have a nodecontentchange:
vParent = instance.vParent;
vParent && vParent._emit(EV_CONTENT_CHANGE);
// in case of removal we need to emit EV_REMOVED for all children right now
// for they will be actually removed silently after a delay of 1 minute
(evt===EV_REMOVED) && _emitDestroyChildren(instance);
if (!BATCH_WILL_RUN) {
BATCH_WILL_RUN = true;
async(function() {
_batchEmit();
});
}
}
return instance;
},
/**
* Inserts `newVNode` before `refVNode`.
*
* Syncs with the DOM.
*
* @method _insertBefore
* @param newVNode {vnode} vnode to insert
* @param refVNode {vnode} The vnode before which newVNode should be inserted.
* @private
* @return {Node} the Node being inserted (equals domNode)
* @since 0.0.1
*/
_insertBefore: function(newVNode, refVNode) {
var instance = this,
domNode = newVNode.domNode,
index = instance.vChildNodes.indexOf(refVNode),
noProccessScript, scriptContent;
if ((newVNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (newVNode.attrs && newVNode.attrs.src) ? newVNode.attrs.src : newVNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
if (index!==-1) {
newVNode._moveToParent(instance, index);
instance.domNode._insertBefore(domNode, refVNode.domNode);
(newVNode.nodeType===3) && instance._normalize();
if (newVNode.nodeType===1) {
newVNode._addToTaglist();
newVNode._emit(EV_INSERTED);
}
return domNode;
}
else {
console.warn('trying to insert before, but no ref-node found. Will append the new node');
return instance._appendChild(newVNode);
}
}
},
/**
* Moves the vnode from its current parent.vChildNodes list towards a new parent vnode at the specified position.
*
* Does NOT sync with the dom.
*
* @method _moveToParent
* @param parentVNode {vnode} the parent-vnode
* @param [index] {Number} the position of the child. When not specified, it will be appended.
* @private
* @chainable
* @since 0.0.1
*/
_moveToParent: function(parentVNode, index) {
var instance = this,
vParent = instance.vParent;
instance._deleteFromParent();
instance.vParent = parentVNode;
parentVNode.vChildNodes || (parentVNode.vChildNodes=[]);
(typeof index==='number') ? parentVNode.vChildNodes.insertAt(instance, index) : (parentVNode.vChildNodes[parentVNode.vChildNodes.length]=instance);
// force to recalculate the vChildren on a next call:
vParent && (instance.nodeType===1) && (vParent._vChildren = null);
// force to recalculate the vChildren on a next call:
parentVNode && (instance.nodeType===1) && (parentVNode._vChildren=null);
return instance;
},
/**
* Removes empty TextNodes and merges following TextNodes inside the vnode.
*
* Syncs with the dom.
*
* @method _normalize
* @private
* @chainable
* @since 0.0.1
*/
_normalize: function() {
var instance = this,
domNode = instance.domNode,
vChildNodes = instance.vChildNodes,
changed = false,
i, preChildNode, vChildNode;
if (!instance._unNormalizable && vChildNodes) {
for (i=vChildNodes.length-1; i>=0; i--) {
vChildNode = vChildNodes[i];
preChildNode = vChildNodes[i-1]; // i will get the value `-1` eventually, which leads into undefined preChildNode
if (vChildNode.nodeType===3) {
if (vChildNode.text==='') {
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
else if (preChildNode && preChildNode.nodeType===3) {
preChildNode.text += vChildNode.text;
preChildNode.domNode.nodeValue = unescapeEntities(preChildNode.text);
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
}
}
}
changed && instance._emit(EV_CONTENT_CHANGE);
return instance;
},
/**
* Makes the vnode `normalizable`. Could be set to `false` when batch-inserting nodes, while `normalizaing` manually at the end.
* Afterwards, you should always reset `normalizable` to true.
*
* @method _normalizable
* @param value {Boolean} whether the vnode should be normalisable.
* @private
* @chainable
* @since 0.0.1
*/
_normalizable: function(value) {
var instance = this;
value ? (delete instance._unNormalizable) : (instance._unNormalizable=true);
return instance;
},
/**
* Prevents MutationObserver from making the dom sync with the vnode.
* Should be used when manipulating the dom from within the vnode itself (to preventing looping)
*
* @method _noSync
* @chainable
* @private
* @since 0.0.1
*/
_noSync: function() {
var instance = this;
if (!instance._nosync) {
instance._nosync = true;
async(function() {
instance._nosync = false;
});
}
return instance;
},
/**
* Removes the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _removeAttr
* @param attributeName {String}
* @private
* @chainable
* @since 0.0.1
*/
_removeAttr: function(attributeName, suppressItagRender) {
var instance = this,
attributeNameSplitted, ns;
if (instance.isItag && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
console.warn('Not allowed to remove the attribute '+attributeName);
return instance;
}
if (instance.attrs[attributeName]!==undefined) {
delete instance.attrs[attributeName];
// in case of STYLE attribute --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attribute --> special treatment
(attributeName===CLASS) && (instance.classNames={});
if (attributeName===ID) {
delete nodeids[instance.id];
delete instance.id;
}
instance._emit(EV_ATTRIBUTE_REMOVED, attributeName);
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
instance.domNode._removeAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName);
}
else {
instance.domNode._removeAttribute(attributeName);
}
}
return instance;
},
/**
* Removes the vnode's child-vnode from its vChildren and the DOM.
*
* Syncs with the DOM.
*
* @method removeChild
* @param VNode {vnode} the child-vnode to remove
* @private
* @since 0.0.1
*/
_removeChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
hadFocus = domNode && domNode.hasFocus && domNode.hasFocus() && (VNode.attrs['fm-lastitem']==='true'),
parentVNode = VNode.vParent;
VNode._destroy();
_tryRemoveDomNode(instance.domNode, VNode.domNode);
instance._normalize();
// now, reset the focus on focusmanager when needed:
if (hadFocus) {
while (parentVNode && !parentVNode.attrs['fm-manage']) {
parentVNode = parentVNode.vParent;
}
parentVNode && parentVNode.domNode.focus();
}
},
/**
* Replaces the current vnode at the parent.vChildNode list by `newVNode`
*
* Does NOT sync with the dom.
*
* @method _replaceAtParent
* @param newVNode {Object} the new vnode which should take over the place of the current vnode
* @private
* @chainable
* @since 0.0.1
*/
_replaceAtParent: function(newVNode) {
var instance = this,
vParent = instance.vParent,
vChildNodes, index;
if (vParent && (vChildNodes=vParent.vChildNodes)) {
index = vChildNodes.indexOf(instance);
// force to recalculate the vChildren on a next call:
((instance.nodeType===1) || (newVNode.nodeType===1)) && (instance.vParent._vChildren=null);
vChildNodes[index] = newVNode;
}
return instance._destroy();
},
/**
* Sets the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _setAttr
* @param attributeName {String}
* @param value {String} the value for the attributeName
* @param [force=false] {Boolean} force the attribute to be set, even if restrictions would deny it
* @private
* @chainable
* @since 0.0.1
*/
_setAttr: function(attributeName, value, force, suppressItagRender) {
var instance = this,
extractStyle, extractClass,
attrs = instance.attrs,
prevVal = attrs[attributeName],
domNode = instance.domNode,
attributeNameSplitted, ns;
if (instance.isItag && !force && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
if (prevVal!=value) {
console.warn('Not allowed to set the attribute '+attributeName);
}
return instance;
}
// don't check by !== --> value isn't parsed into a String yet
if (prevVal && ((value===undefined) || (value===null))) {
instance._removeAttr(attributeName, suppressItagRender);
return instance;
}
// attribute-values are always Strings:
value = String(value);
// attribute-values will be stored without " or '
value = value.replace(/"/g, '"').replace(/'/g, "'");
if (prevVal!=value) {
attrs[attributeName] = value;
// in case of STYLE attribute --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(value);
value = extractStyle.attrStyle;
if (value) {
attrs.style = value;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attribute --> special treatment
extractClass = extractor.extractClass(value);
value = extractClass.attrClass;
if (value) {
attrs[CLASS] = value;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (delete nodeids[instance.id]);
instance.id = value;
nodeids[value] = domNode;
}
instance._emit(prevVal ? EV_ATTRIBUTE_CHANGED : EV_ATTRIBUTE_INSERTED, attributeName, value, prevVal);
// when set in the dom --> quotes need to be set as "
value = value.replace(/"/g, '"');
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
domNode._setAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName, value);
}
else {
domNode._setAttribute(attributeName, value);
}
}
return instance;
},
/**
* Redefines the attributes of both the vnode as well as its related dom-node. The new
* definition replaces any previous attributes (without touching unmodified attributes).
* the `is` attribute cannot be changed for itags.
*
* Syncs the new vnode's attributes with the dom.
*
* @method _setAttrs
* @param newAttrs {Object|Array} the new attributes to be set
* @private
* @chainable
* @since 0.0.1
*/
_setAttrs: function(newAttrs, suppressItagRender) {
// does sync the DOM
var instance = this,
attrsObj, attr, attrs, i, key, keys, len, value;
if (instance.nodeType!==1) {
return;
}
instance._noSync();
attrs = instance.attrs;
attrs.id && (delete nodeids[attrs.id]);
if (Object.isObject(newAttrs)) {
attrsObj = newAttrs;
}
else {
attrsObj = {};
len = newAttrs.length;
for (i=0; i<len; i++) {
attr = newAttrs[i];
attrsObj[attr.name] = attr.value;
}
}
if (instance.isItag && !suppressItagRender) {
if (attrs.is) {
attrsObj.is = attrs.is;
}
else {
delete attrsObj.is;
delete attrsObj.Is;
delete attrsObj.iS;
delete attrsObj.IS;
}
}
// first _remove the attributes that are no longer needed.
// quickest way for object iteration: http://jsperf.com/object-keys-iteration/20
keys = Object.keys(attrs);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
attrsObj[key] || instance._removeAttr(key, suppressItagRender);
}
// next: every attribute that differs: redefine
keys = Object.keys(attrsObj);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
value = attrsObj[key];
(attrs[key]===value) || instance._setAttr(key, value, suppressItagRender);
}
return instance;
},
/**
* Redefines the childNodes of both the vnode as well as its related dom-node. The new
* definition replaces any previous nodes. (without touching unmodified nodes).
*
* Syncs the new vnode's childNodes with the dom.
*
* @method _setChildNodes
* @param newVChildNodes {Array} array with vnodes which represent the new childNodes
* @private
* @chainable
* @since 0.0.1
*/
_setChildNodes: function(newVChildNodes, suppressItagRender, removeSystemElements) {
// does sync the DOM
var instance = this,
vChildNodes = instance.vChildNodes || [],
domNode = instance.domNode,
forRemoval = [],
i, oldChild, newChild, newLength, len, len2, childDomNode, nodeswitch, bkpAttrs, cleanupStyle,
process, bkpChildNodes, needNormalize, prevSuppress, scriptContent, _scripts, scriptLen, j;
instance._noSync();
// first: reset ._vChildren --> by making it empty, its getter will refresh its list on a next call
instance._vChildren = null;
// if newVChildNodes is undefined, then we assume it to be empty --> an empty array
newVChildNodes || (newVChildNodes=[]);
// quickest way to loop through array is by using for loops: http://jsperf.com/array-foreach-vs-for-loop/5
len = vChildNodes.length;
// we need to add the systemNodes -if any- to the new newVChildNodes: they should be retained.
// SystemNodes are always places at the beginning, so they won't get reshuffled:
if (!removeSystemElements) {
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
if (oldChild._systemNode) {
newVChildNodes.insertAt(oldChild, i);
}
else {
break;
}
}
}
newLength = newVChildNodes.length;
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
childDomNode = oldChild.domNode;
if (i < newLength) {
newChild = newVChildNodes[i];
newChild.vParent || (newChild.vParent=instance);
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
/*jshint boss:true */
switch (nodeswitch=NODESWITCH[oldChild.nodeType][newChild.nodeType]) {
/*jshint boss:false */
case 1: // oldNodeType==Element, newNodeType==Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
if ((oldChild.tag!==newChild.tag) ||
((oldChild.tag===newChild.tag) && oldChild.isItag && (oldChild.attrs.is!==newChild.attrs.is))) {
// new tag --> completely replace
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
/*jshint proto:true */
oldChild.isItag && oldChild.domNode.destroyUI && oldChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
newChild.attrs = {}; // reset to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
oldChild._replaceAtParent(newChild);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
else {
// same tag --> only update what is needed
// NOTE: when this._unchangableAttrs exists, an itag-element syncs its UI -->
if (oldChild._data) {
// we might need to set the class `itag-rendered` when the attributeData says so:
// this happens when an itag gets refreshed with an unrendered definition
if (oldChild._data.itagRendered && !newChild.hasClass('itag-rendered')) {
newChild.classNames['itag-rendered'] = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' '+'itag-rendered';
}
else {
newChild.attrs[CLASS] = 'itag-rendered';
}
}
// we might need to set the class `focussed` when the attributeData says so:
// this happens when an itag gets rerendered: its renderFn doesn't know if any elements
// were focussed
if (oldChild._data.focussed && !newChild.hasClass('focussed')) {
newChild.classNames.focussed = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' ' + 'focussed';
}
else {
newChild.attrs[CLASS] = 'focussed';
}
}
if (oldChild._data['fm-tabindex']) {
// node has the tabindex set by the focusmanager,
// but that info might got lost with re-rendering of the new element
newChild.attrs.tabindex = '0';
}
}
if (oldChild.isItag) {
prevSuppress = DOCUMENT._suppressMutationEvents || false;
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
/*jshint proto:true */
newChild.domNode.destroyUI && newChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
oldChild._setAttrs(newChild.attrs, suppressItagRender);
newChild._destroy(true); // destroy through the vnode and removing from DOCUMENT._itagList
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(prevSuppress);
}
else {
oldChild._setAttrs(newChild.attrs, suppressItagRender);
// next: sync the vChildNodes:
oldChild._setChildNodes(newChild.vChildNodes, suppressItagRender);
}
// reset ref. to the domNode, for it might have been changed by newChild:
oldChild.id && (nodeids[oldChild.id]=childDomNode);
newVChildNodes[i] = oldChild;
}
}
break;
case 2: // oldNodeType==Element, newNodeType==TextNode
// case2 and case3 should be treated the same
case 3: // oldNodeType==Element, newNodeType==Comment
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
oldChild._replaceAtParent(newChild);
instance._emit(EV_CONTENT_CHANGE);
break;
case 4: // oldNodeType==TextNode, newNodeType==Element
// case4 and case7 should be treated the same
case 7: // oldNodeType==Comment, newNodeType==Element
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
// oldChild.isVoid = newChild.isVoid;
// delete oldChild.text;
instance._emit(EV_CONTENT_CHANGE);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
break;
case 5: // oldNodeType==TextNode, newNodeType==TextNode
// case5 and case9 should be treated the same
case 9: // oldNodeType==Comment, newNodeType==Comment
if (oldChild.text!==newChild.text) {
oldChild.text = newChild.text;
oldChild.domNode.nodeValue = unescapeEntities(newChild.text);
instance._emit(EV_CONTENT_CHANGE);
}
newVChildNodes[i] = oldChild;
break;
case 6: // oldNodeType==TextNode, newNodeType==Comment
// case6 and case8 should be treated the same
case 8: // oldNodeType==Comment, newNodeType==TextNode
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = oldChild.vParent;
instance._emit(EV_CONTENT_CHANGE);
}
if ((nodeswitch===2) || (nodeswitch===5) || (nodeswitch===8)) {
needNormalize = true;
}
}
else {
// _remove previous definition
_tryRemoveDomNode(domNode, oldChild.domNode);
// the oldChild needs to be removed, however, this cannot be done right now, for it would effect the loop
// so we store it inside a hash to remove it later
forRemoval[forRemoval.length] = oldChild;
}
}
// now definitely remove marked childNodes:
len2 = forRemoval.length;
for (i=0; i<len2; i++) {
forRemoval[i]._destroy();
}
// now we add all new vChildNodes that go beyond `len`:
for (i = len; i < newLength; i++) {
newChild = newVChildNodes[i];
newChild.vParent = instance;
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
switch (newChild.nodeType) {
case 1: // Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
domNode._appendChild(newChild.domNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
break;
case 3: // TextNode
needNormalize = true;
// we need to break through --> no `break`
/* falls through */
default: // TextNode or CommentNode
// newChild.domNode.nodeValue = newChild.text;
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
domNode._appendChild(newChild.domNode);
instance._emit(EV_CONTENT_CHANGE);
}
newChild.storeId();
}
instance.vChildNodes = newVChildNodes;
needNormalize && instance._normalize();
cleanupStyle && instance._cleanupStyle();
return instance;
},
_setUnchangableAttrs: function(unchangableObj) {
this._unchangableAttrs = unchangableObj;
}
};
//---- properties ------------------------------------------------------------------
/**
* A hash of all the `attributes` of the vnode's representing dom-node.
*
* @property attrs
* @type Object
* @since 0.0.1
*/
/**
* Hash with all the classes of the vnode. Every class represents a key, all values are set `true`.
*
* @property classNames
* @type Object
* @since 0.0.1
*/
/**
* The `id` of the vnode's representing dom-node (if any).
*
* @property id
* @type String
* @since 0.0.1
*/
/**
* Tells whether tag is a void Element. Examples are: `br`, `img` and `input`. Non-void Elements are f.e. `div` and `table`.
* For TextNodes and CommentNodes, this property is `undefined`.
*
* @property isVoid
* @type Boolean
* @since 0.0.1
*/
/**
* The `nodeType` of the vnode's representing dom-node (1===ElementNode, 3===TextNode, 8===CommentNode).
*
* @property nodeType
* @type Number
* @since 0.0.1
*/
/**
* The `tag` of the vnode's representing dom-node (allways uppercase).
*
* @property tag
* @type String
* @since 0.0.1
*/
/**
* The `content` of the vnode's representing dom-node, in case it is a TextNode or CommentNode.
* Equals dom-node.nodeValue.
*
* Is `undefined` for ElementNodes.
*
* @property text
* @type String
* @since 0.0.1
*/
/**
* Hash with all the childNodes (vnodes). vChildNodes are any kind of vnodes (nodeType===1, 3 or 8)
*
* @property vChildNodes
* @type Array
* @since 0.0.1
*/
/**
* The underlying `dom-node` that the vnode represents.
*
* @property domNode
* @type domNode
* @since 0.0.1
*/
/**
* vnode's parentNode (defined as a vnode itself).
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
Object.defineProperties(vNodeProto, {
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property innerHTML
* @type String
* @since 0.0.1
*/
innerHTML: {
get: function() {
return this.getHTML();
},
set: function(v) {
this.setHTML(v);
}
},
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property nodeValue
* @type String
* @since 0.0.1
*/
nodeValue: {
get: function() {
var instance = this;
return ((instance.nodeType===3) || (instance.nodeType===8)) ? instance.text : null;
},
set: function(v) {
var instance = this,
newTextContent, prevTextContent;
if ((instance.nodeType===3) || (instance.nodeType===8)) {
prevTextContent = instance.domNode.textContent;
instance.domNode.textContent = v;
// set .text AFTER the dom-node is updated --> the content might be escaped!
newTextContent = instance.text = instance.domNode.textContent;
(newTextContent!==prevTextContent) && instance._emit(EV_CONTENT_CHANGE);
}
}
},
/**
* Gets or sets the outerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property outerHTML
* @type String
* @since 0.0.1
*/
outerHTML: {
get: function() {
return this.getOuterHTML();
},
set: function(v) {
var instance = this,
vParent = instance.vParent,
id = instance.attrs.id,
vnode, vnodes, bkpAttrs, bkpChildNodes, i, len, vChildNodes, isLastChildNode, index, refDomNode;
if ((instance.nodeType!==1) || !vParent) {
return;
}
instance._noSync();
vChildNodes = vParent.vChildNodes;
index = vChildNodes.indexOf(instance);
isLastChildNode = (index===(vChildNodes.length-1));
isLastChildNode || (refDomNode=vChildNodes[index+1].domNode);
vnodes = htmlToVNodes(v, vNodeProto, vParent.ns, vParent);
len = vnodes.length;
if (len>0) {
// the first vnode will replace the current instance:
vnode = vnodes[0];
if (vnode.nodeType===1) {
if (vnode.tag!==instance.tag) {
// new tag --> completely replace
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
id && (delete nodeids[id]);
vnode.attrs = {}; // reset to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
// vnode.attrs = bkpAttrs;
// vnode.vChildNodes = bkpChildNodes;
vnode.id && (nodeids[vnode.id]=vnode.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
else {
instance._setAttrs(vnode.attrs);
instance._setChildNodes(vnode.vChildNodes);
}
}
else {
id && (delete nodeids[id]);
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
}
for (i=1; i<len; i++) {
vnode = vnodes[i];
switch (vnode.nodeType) {
case 1: // Element
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
vnode.attrs = {}; // reset, to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._insertBefore(vnode.domNode, refDomNode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
break;
default: // TextNode or CommentNode
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._appendChild(vnode.domNode, refDomNode);
}
vnode.storeId();
vnode._moveToParent(vParent, index+i);
}
}
},
/**
* Gets or sets the innerContent of the Node as plain text.
*
* The setter syncs with the DOM.
*
* @property textContent
* @type String
* @since 0.0.1
*/
textContent: {
get: function() {
var instance = this,
text = '',
vChildNodes = instance.vChildNodes,
len, i, vChildNode;
if (instance.nodeType===1) {
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
text += (vChildNode.nodeType===3) ? vChildNode.text : ((vChildNode.nodeType===1) ? vChildNode.textContent : '');
}
}
else {
text = instance.text;
}
return text;
},
set: function(v) {
var vnode = Object.create(vNodeProto);
vnode.domNode = DOCUMENT.createTextNode(v);
// create circular reference:
vnode.domNode._vnode = vnode;
vnode.nodeType = 3;
vnode.text = vnode.domNode.textContent;
this._setChildNodes([vnode]);
}
},
/**
* Hash with all the children (vnodes). vChildren are vnodes that have a representing dom-node that is an HtmlElement (nodeType===1)
*
* @property vChildren
* @type Array
* @since 0.0.1
*/
vChildren: {
get: function() {
var instance = this,
children = instance._vChildren,
vChildNode, vChildNodes, i, len;
vChildNodes = instance.vChildNodes;
if (vChildNodes && !children) {
children = instance._vChildren = [];
len = vChildNodes.length;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
(vChildNode.nodeType===1) && (children[children.length]=vChildNode);
}
}
children || (children = instance._vChildren = []);
return children;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirst
* @type vnode
* @since 0.0.1
*/
vFirst: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstChild;
}
},
/**
* Reference to the first vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirstChild
* @type vnode
* @since 0.0.1
*/
vFirstChild: {
get: function() {
return (this.vChildNodes && this.vChildNodes[0]) || null;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vFirstElement
* @type vnode
* @since 0.0.1
*/
vFirstElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstElementChild;
}
},
/**
* Reference to the first vChild, where the related dom-node an Element (nodeType===1).
*
* @property vFirstElementChild
* @type vnode
* @since 0.0.1
*/
vFirstElementChild: {
get: function() {
return this.vChildren[0] || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLast
* @type vnode
* @since 0.0.1
*/
vLast: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastChild;
}
},
/**
* Reference to the last vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLastChild
* @type vnode
* @since 0.0.1
*/
vLastChild: {
get: function() {
var vChildNodes = this.vChildNodes;
return (vChildNodes && vChildNodes[vChildNodes.length-1]) || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vLastElement
* @type vnode
* @since 0.0.1
*/
vLastElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastElementChild;
}
},
/**
* Reference to the last vChild, where the related dom-node an Element (nodeType===1).
*
* @property vLastElementChild
* @type vnode
* @since 0.0.1
*/
vLastElementChild: {
get: function() {
var vChildren = this.vChildren;
return vChildren[vChildren.length-1] || null;
}
},
/**
* the Parent vnode
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
/**
* Reference to the next of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vNext
* @type vnode
* @since 0.0.1
*/
vNext: {
get: function() {
return _findNodeSibling(this, true);
}
},
/**
* Reference to the next of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vNextElement
* @type vnode
* @since 0.0.1
*/
vNextElement: {
get: function() {
return _findElementSibling(this, true);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vPrevious
* @type vnode
* @since 0.0.1
*/
vPrevious: {
get: function() {
return _findNodeSibling(this);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vPreviousElement
* @type vnode
* @since 0.0.1
*/
vPreviousElement: {
get: function() {
return _findElementSibling(this);
}
}
});
return vNodeProto;
};
},{"./attribute-extractor.js":2970,"./html-parser.js":2974,"./vdom-ns.js":2976,"js-ext/extra/hashmap.js":2928,"js-ext/extra/lightmap.js":2929,"js-ext/lib/array.js":2930,"js-ext/lib/object.js":2931,"js-ext/lib/string.js":2933,"polyfill":2950,"utils/lib/timers.js":2953}],2978:[function(require,module,exports){
arguments[4][100][0].apply(exports,arguments)
},{"./partials/extend-document.js":2972,"./partials/extend-element.js":2973,"./partials/node-parser.js":2975,"js-ext/extra/hashmap.js":2928,"js-ext/lib/object.js":2931,"utils/lib/timers.js":2953}],2979:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2980,"./lib/timers.js":2981}],2980:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2985,"polyfill/polyfill-base.js":2984}],2981:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2984}],2982:[function(require,module,exports){
module.exports=require(14)
},{}],2983:[function(require,module,exports){
module.exports=require(15)
},{}],2984:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2982,"./lib/window.console.js":2983}],2985:[function(require,module,exports){
module.exports=require(4)
},{}],2986:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":2985,"polyfill/polyfill-base.js":2989,"utils":2990}],2987:[function(require,module,exports){
module.exports=require(14)
},{}],2988:[function(require,module,exports){
module.exports=require(15)
},{}],2989:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2987,"./lib/window.console.js":2988}],2990:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":2991,"./lib/timers.js":2992}],2991:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2985,"polyfill/polyfill-base.js":2995}],2992:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":2995}],2993:[function(require,module,exports){
module.exports=require(14)
},{}],2994:[function(require,module,exports){
module.exports=require(15)
},{}],2995:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":2993,"./lib/window.console.js":2994}],2996:[function(require,module,exports){
arguments[4][5][0].apply(exports,arguments)
},{"event-dom":2997,"js-ext/extra/classes.js":3092,"js-ext/extra/hashmap.js":3093,"js-ext/lib/object.js":3094,"js-ext/lib/promise.js":3095,"js-ext/lib/string.js":3096,"polyfill":3108,"utils/lib/timers.js":3109,"vdom":3165}],2997:[function(require,module,exports){
"use strict";
/**
* Integrates DOM-events to event. more about DOM-events:
* http://www.smashingmagazine.com/2013/11/12/an-introduction-to-dom-events/
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @example
* Event = require('event-dom')(window);
*
* @module event
* @submodule event-dom
* @class Event
* @since 0.0.1
*/
var NAME = '[event-dom]: ',
Event = require('event'),
later = require('utils').later,
createHashMap = require('js-ext/extra/hashmap.js').createMap,
OUTSIDE = 'outside',
REGEXP_NODE_ID = /^#\S+$/,
REGEXP_EXTRACT_NODE_ID = /#(\S+)/,
REGEXP_UI_OUTSIDE = /^.+outside$/,
TIME_BTN_PRESSED = 200,
PURE_BUTTON_ACTIVE = 'pure-button-active',
UI = 'UI:',
NODE = 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
TAP = 'tap',
CLICK = 'click',
RIGHTCLICK = 'right'+CLICK,
CENTERCLICK = 'center'+CLICK,
EV_REMOVED = UI+NODE+REMOVE,
EV_INSERTED = UI+NODE+INSERT,
EV_CONTENT_CHANGE = UI+NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = UI+ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = UI+ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = UI+ATTRIBUTE+INSERT,
mutationEventsDefined = false,
NO_DEEP_SEARCH = {},
/*
* Internal hash containing all DOM-events that are listened for (at `document`).
*
* DOMEvents = {
* 'tap': callbackFn,
* 'mousemove': callbackFn,
* 'keypress': callbackFn
* }
*
* @property DOMEvents
* @default {}
* @type Object
* @private
* @since 0.0.1
*/
DOMEvents = {};
require('js-ext/lib/string.js');
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('polyfill/polyfill-base.js');
module.exports = function (window) {
var DOCUMENT = window.document,
_domSelToFunc, _evCallback, _findCurrentTargets, _preProcessor, _setupEvents, _setupMutationListener, _teardownMutationListener,
_setupDomListener, _teardownDomListener, SORT, _sortFunc, _sortFuncReversed, _getSubscribers, _selToFunc, MUTATION_EVENTS, preventClick;
require('vdom')(window);
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.EventDom) {
return Event; // Event was already extended
}
console.warn('event-dom 12');
MUTATION_EVENTS = [EV_REMOVED, EV_INSERTED, EV_CONTENT_CHANGE, EV_ATTRIBUTE_REMOVED, , EV_ATTRIBUTE_CHANGED, EV_ATTRIBUTE_INSERTED];
/*
* Transforms the selector to a valid function
*
* @method _selToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_selToFunc = function(customEvent, subscriber) {
Event._sellist.some(function(selFn) {
return selFn(customEvent, subscriber);
});
};
/*
* Creates a filterfunction out of a css-selector. To be used for catching any dom-element, without restrictions
* of any context (like Parcels can --> Parcel.Event uses _parcelSelToDom instead)
* On "non-outside" events, subscriber.t is set to the node that first matches the selector
* so it can be used to set as e.target in the final subscriber
*
* @method _domSelToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_domSelToFunc = function(customEvent, subscriber) {
// this stage is runned during subscription
var outsideEvent = REGEXP_UI_OUTSIDE.test(customEvent),
selector = subscriber.f,
context = subscriber.o,
vnode = subscriber.o.vnode,
isCustomElement = vnode && vnode.isItag,
visibleContent = isCustomElement && !vnode.domNode.contentHidden,
nodeid, byExactId, newTarget, deepSearch;
isCustomElement = false;
console.log(NAME, '_domSelToFunc type of selector = '+typeof selector);
// note: selector could still be a function: in case another subscriber
// already changed it.
if ((!selector && !isCustomElement) || (typeof selector === 'function')) {
subscriber.n || (subscriber.n = isCustomElement ? context : DOCUMENT);
return true;
}
selector || (selector='');
nodeid = selector.match(REGEXP_EXTRACT_NODE_ID);
nodeid ? (subscriber.nId=nodeid[1]) : (subscriber.n=isCustomElement ? context : DOCUMENT);
byExactId = REGEXP_NODE_ID.test(selector);
deepSearch = !NO_DEEP_SEARCH[customEvent];
// set the selector to `subscriber._s` so that e.currentTarget can calculate it:
subscriber._s = selector;
subscriber.f = function(e) {
// this stage is runned when the event happens
console.log(NAME, '_domSelToFunc inside filter. selector: '+selector);
var node = e.target,
vnode = node.vnode,
character1 = selector && selector.substr(1),
match = false;
if (!isCustomElement || visibleContent || context.contains(node)) {
if (selector==='') {
match = true;
}
else {
// e.target is the most deeply node in the dom-tree that caught the event
// our listener uses `selector` which might be a node higher up the tree.
// we will reset e.target to this node (if there is a match)
// note that e.currentTarget will always be `document` --> we're not interested in that
// also, we don't check for `node`, but for node.matchesSelector: the highest level `document`
// is not null, yet it doesn't have .matchesSelector so it would fail
// we DON'T want this for the focus and blur event!
if (deepSearch) {
if (vnode) {
// we go through the vdom
if (!vnode.removedFromDOM) {
while (vnode && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = vnode.domNode;
}
vnode = vnode.vParent;
}
}
}
else {
// we go through the dom
while (node.matchesSelector && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the dom');
match = byExactId ? (node.id===character1) : node.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = node;
}
node = node.parentNode;
}
}
}
else {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
if (vnode) {
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
}
else {
match = node.matchesSelector && (byExactId ? (node.id===character1) : node.matchesSelector(selector));
}
match && (e.sourceTarget=node);
}
}
}
if (outsideEvent && !match) {
// there is a match for the outside-event:
// we need to set e.sourceTarget and e.target:
newTarget = DOCUMENT.getElement(selector, true);
if (newTarget) {
e.sourceTarget = node;
subscriber.t = newTarget;
}
else {
// make return `false` because the selector is not in the dom
match = true;
}
}
console.log(NAME, '_domSelToFunc filter returns '+(!outsideEvent ? match : !match));
return !outsideEvent ? match : !match;
};
return true;
};
// at this point, we need to find out what are the current node-refs. whenever there is
// a filter that starts with `#` --> in those cases we have a bubble-chain, because the selector isn't
// set up with `document` at its root.
// we couldn't do this at time of subscribtion, for the nodes might not be there at that time.
// however, we only need to do this once: we store the value if we find them
// no problem when the nodes leave the dom later: the previous filter wouldn't pass
_findCurrentTargets = function(subscribers) {
console.log(NAME, '_findCurrentTargets');
subscribers.forEach(
function(subscriber) {
console.log(NAME, '_findCurrentTargets for single subscriber. nId: '+subscriber.nId);
subscriber.nId && (subscriber.n=DOCUMENT.getElementById(subscriber.nId, true));
}
);
};
/*
* Generates an event through our Event-system. Does the actual transportation from DOM-events
* into our Eventsystem. It also looks at the response of our Eventsystem: if our system
* halts or preventDefaults the customEvent, then the original DOM-event will be preventDefaulted.
*
* @method _evCallback
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_evCallback = function(e) {
console.log(NAME, '_evCallback');
var allSubscribers = Event._subs,
eType = e.type,
eventobject, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs, subsOutside,
subscribers, eventobjectOutside, wildcard_named_subsOutside, customEvent, eventName, which;
eventName = eType;
// first: a `click` event might be needed to transformed into `rightclick`:
if (eventName===CLICK) {
which = e.which;
(which===2) && (eventName=CENTERCLICK);
(which===3) && (eventName=RIGHTCLICK);
}
if (eventName==='tap') {
// prevent the next click-event
preventClick = true;
e.clientX || (e.clientX = e.center && e.center.x);
e.clientY || (e.clientY = e.center && e.center.y);
}
else if (preventClick && (eventName===CLICK)) {
preventClick = false;
return;
}
if (eventName===CLICK) {
eventName = 'tap';
e.center = {
x: e.clientX,
y: e.clientY
};
e.eventType = 4;
e.pointerType = 'mouse';
e.tapCount = 1;
}
customEvent = 'UI:'+eventName;
subs = allSubscribers[customEvent];
wildcard_named_subs = allSubscribers['*:'+eventName];
named_wildcard_subs = allSubscribers['UI:*'];
wildcard_wildcard_subs = allSubscribers['*:*'];
// Emit the dom-event though our eventsystem:
// NOTE: emit() needs to be synchronous! otherwise we wouldn't be able
// to preventDefault in time
//
// e = eventobject from the DOM-event OR gesture-event
// eventobject = eventobject from our Eventsystem, which get returned by calling `emit()`
// now so the work:
subscribers = _getSubscribers(e, true, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
eventobject = Event._emit(e.target, customEvent, e, subscribers, [], _preProcessor, false, true);
// now check outside subscribers
subsOutside = allSubscribers[customEvent+OUTSIDE];
wildcard_named_subsOutside = allSubscribers['*:'+eventName+OUTSIDE];
subscribers = _getSubscribers(e, true, subsOutside, wildcard_named_subsOutside);
eventobjectOutside = Event._emit(e.target, customEvent+OUTSIDE, e, subscribers, [], _preProcessor, false, true);
// if eventobject was preventdefaulted or halted: take appropriate action on
// the original dom-event. Note: only the original event can caused this, not the outsideevent
// stopPropagation on the original eventobject has no impact on our eventsystem, but who know who else is watching...
// be carefull though: not all gesture events have e.stopPropagation
eventobject.status.halted && e.stopPropagation && e.stopPropagation();
// now we might need to preventDefault the original event.
// be carefull though: not all gesture events have e.preventDefault
if ((eventobject.status.halted || eventobject.status.defaultPrevented || eventobject.status.defaultPreventedContinue) && e.preventDefault) {
e.preventDefault();
}
if (eventobject.status.ok) {
// last step: invoke the aftersubscribers
// we need to do this asynchronous: this way we pass them AFTER the DOM-event's defaultFn
// also make sure to paas-in the payload of the manipulated eventobject
subscribers = _getSubscribers(e, false, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent, eventobject, [], subscribers, _preProcessor, true), 10);
// now check outside subscribers
subscribers = _getSubscribers(e, false, subsOutside, wildcard_named_subsOutside);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent+OUTSIDE, eventobjectOutside, [], subscribers, _preProcessor, true), 10);
}
};
/*
* Creates an array of subscribers in the right order, conform their position in the DOM.
* Only subscribers that match the filter are involved.
*
* @method _getSubscribers
* @param e {Object} eventobject
* @param before {Boolean} whether it is a before- or after-subscriber
* @param subs {Array} array with subscribers
* @param wildcard_named_subs {Array} array with subscribers
* @param named_wildcard_subs {Array} array with subscribers
* @param wildcard_wildcard_subs {Array} array with subscribers
* @private
* @since 0.0.1
*/
_getSubscribers = function(e, before, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs) {
var subscribers = [],
beforeOrAfter = before ? 'b' : 'a',
saveConcat = function(extrasubs) {
extrasubs && extrasubs[beforeOrAfter] && (subscribers=subscribers.concat(extrasubs[beforeOrAfter]));
};
saveConcat(subs);
saveConcat(wildcard_named_subs);
saveConcat(named_wildcard_subs);
saveConcat(wildcard_wildcard_subs);
if (subscribers.length>0) {
subscribers = function(array, testFunc) {
// quickest way to filter an array: see http://jsperf.com/array-filter-performance/4
var filtered = array.slice(0), i;
for (i=array.length-1; i>=0; i--) {
console.log(NAME, 'filtercheck for subscriber');
testFunc(array[i]) || filtered.splice(i, 1);
}
return filtered;
}(subscribers, function(subscriber) {return (!subscriber.f || subscriber.f.call(subscriber.o, e));});
if (subscribers.length>0) {
// _findCurrentTargets(subscribers);
// sorting, based upon the sortFn
subscribers.sort(SORT);
}
}
return subscribers;
};
/*
* Sets e.target and e.sourceTarget for the single subscriber.
* Needs to be done for evenry single subscriber, because with a single event, these values change for each subscriber
*
* @method _preProcessor
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_preProcessor = function(subscriber, e) {
console.log(NAME, '_preProcessor');
// inside the aftersubscribers, we may need exit right away.
// this would be the case whenever stopPropagation or stopImmediatePropagation was called
// in case the subscribernode equals the node on which stopImmediatePropagation was called: return true
var propagationStopped, immediatePropagationStopped,
targetnode = (subscriber.t || subscriber.n);
immediatePropagationStopped = e.status.immediatePropagationStopped;
if (immediatePropagationStopped && ((immediatePropagationStopped===targetnode) || !immediatePropagationStopped.contains(targetnode))) {
console.log(NAME, '_preProcessor will return true because of immediatePropagationStopped');
return true;
}
// in case the subscribernode does not fall within or equals the node on which stopPropagation was called: return true
propagationStopped = e.status.propagationStopped;
if (propagationStopped && (propagationStopped!==targetnode) && !propagationStopped.contains(targetnode)) {
console.log(NAME, '_preProcessor will return true because of propagationStopped');
return true;
}
e._s = subscriber._s;
// now we might need to set e.target to the right node:
// the filterfunction might have found the true domnode that should act as e.target
// and set it at subscriber.t
// also, we need to backup the original e.target: this one should be reset when
// we encounter a subscriber with its own filterfunction instead of selector
if (subscriber.t) {
e.sourceTarget || (e.sourceTarget=e.target);
e.target = subscriber.t;
}
else {
e.sourceTarget && (e.target=e.sourceTarget);
}
return false;
};
/*
* Transports DOM-events to the Event-system. Catches events at their most early stage:
* their capture-phase. When these events happen, a new customEvent is generated by our own
* Eventsystem, by calling _evCallback(). This way we keep DOM-events and our Eventsystem completely separated.
*
* @method _setupDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_setupDomListener = function(customEvent, subscriber) {
console.log(NAME, '_setupDomListener');
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0],
eventName = eventSplitted[1],
outsideEvent = REGEXP_UI_OUTSIDE.test(eventName);
// be careful: anyone could also register an `outside`-event.
// in those cases, the DOM-listener must be set up without `outside`
outsideEvent && (eventName=eventName.substring(0, eventName.length-7));
// if eventName equals `mouseover` or `mouseleave` then we quit:
// people should use `mouseover` and `mouseout`
if ((eventName==='mouseenter') || (eventName==='mouseleave')) {
console.warn(NAME, 'Subscription to '+eventName+' not supported, use mouseover and mouseout: this eventsystem uses these non-noisy so they act as mouseenter and mouseleave');
return;
}
// only accept tap-events, yet later on we WILL need to listen for click-events
(eventName===CLICK) && (eventName=TAP);
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(emitterName+':'+eventName+(outsideEvent ? OUTSIDE : ''), subscriber);
// already registered? then return, also return if someone registered for UI:*
if (DOMEvents[eventName] || (eventName==='*')) {
// cautious: one might have registered the event, but not yet the outsideevent.
// in that case: save this setting:
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
return;
}
DOMEvents[eventName] = true;
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
// one exception: windowresize should listen to the window-object
if (eventName==='resize') {
window.addEventListener(eventName, _evCallback);
}
else {
((eventName===RIGHTCLICK) || (eventName===CENTERCLICK)) && (eventName=TAP);
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.addEventListener(eventName, _evCallback, true);
// listen for both `tap` and `click` events to happen
(eventName===TAP) && DOCUMENT.addEventListener(CLICK, _evCallback, true);
}
};
_setupEvents = function() {
var lastFocussed;
// make sure disabled buttons don't work:
Event.before(['tap', 'press'], function(e) {
e.preventDefault();
}, '.pure-button-disabled, button[disabled]');
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.before(
'keydown',
function(e) {
e._buttonPressed = true;
Event.emit(e.target, 'UI:tap', e);
},
function(e) {
var keyCode = e.keyCode;
return (e.target.getTagName()==='BUTTON') && ((keyCode===13) || (keyCode===32));
}
);
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.after(
'tap',
function(e) {
var buttonNode = e.target;
if (e._buttonPressed) {
buttonNode.setClass(PURE_BUTTON_ACTIVE);
e._noRender = true;
// even if the node isn't in the DOM, we can still try to manipulate it:
// the vdom makes sure no errors occur when the node is already removed
later(buttonNode.removeClass.bind(buttonNode, PURE_BUTTON_ACTIVE), TIME_BTN_PRESSED);
}
}
);
// fix activeElement on Mac
Event.before('focus', function(e) {
// will come here more often because of bubblechain
// however, the last pass-through will set the deepest node
lastFocussed = e.target;
});
// patching DOCUMENT.activeElement because it doesn't work well in a Mac: https://developer.mozilla.org/en-US/docs/Web/API/document.activeElement
// DOCUMENT._activeElement is used with the patch for DOCUMENT.activeElement its getter
Event.after('focus', function() {
DOCUMENT._activeElement = lastFocussed;
});
Event.before('blur', function() {
DOCUMENT._activeElement = null;
});
// Note: window.document has no prototype
Object.defineProperty(DOCUMENT, 'activeElement', {
get: function() {
return DOCUMENT._activeElement || DOCUMENT.body;
}
});
};
_setupMutationListener = function() {
DOCUMENT.hasMutationSubs = true;
if (!mutationEventsDefined) {
Event.defineEvent(EV_REMOVED).unPreventable();
Event.defineEvent(EV_INSERTED).unPreventable();
Event.defineEvent(EV_CONTENT_CHANGE).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_REMOVED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_CHANGED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_INSERTED).unPreventable();
mutationEventsDefined = true;
}
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFunc = function(subscriberOne, subscriberTwo) {
return (subscriberTwo.t || subscriberTwo.n).contains(subscriberOne.t || subscriberOne.n) ? -1 : 1;
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFuncReversed = function(subscriberOne, subscriberTwo) {
return (subscriberOne.t || subscriberOne.n).contains(subscriberTwo.t || subscriberTwo.n) ? 1 : -1;
};
/*
* Removes DOM-eventsubscribers from document when they are no longer needed.
*
* @method _teardownDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @private
* @since 0.0.2
*/
_teardownDomListener = function(customEvent) {
var customEventWithoutOutside = customEvent.endsWith(OUTSIDE) ? customEvent.substr(0, customEvent.length-7) : customEvent,
eventSplitted = customEventWithoutOutside.split(':'),
eventName = eventSplitted[1],
stillInUse;
if ((customEventWithoutOutside===CLICK) || (customEventWithoutOutside===RIGHTCLICK) || (customEventWithoutOutside===CENTERCLICK)) {
stillInUse = Event._subs[CLICK] ||
Event._subs[CLICK+OUTSIDE];
Event._subs[RIGHTCLICK] ||
Event._subs[RIGHTCLICK+OUTSIDE],
Event._subs[CENTERCLICK] ||
Event._subs[CENTERCLICK+OUTSIDE];
eventName = CLICK;
}
else {
stillInUse = Event._subs[customEventWithoutOutside] || Event._subs[customEventWithoutOutside+OUTSIDE];
}
if (!stillInUse) {
console.log(NAME, '_teardownDomListener '+customEvent);
// remove eventlistener from `document`
// one exeption: windowresize should listen to the window-object
if (eventName==='resize') {
window.removeEventListener(eventName, _evCallback);
}
else {
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.removeEventListener(eventName, _evCallback, true);
}
delete DOMEvents[eventName];
}
};
_teardownMutationListener = function() {
if (!Event._subs[EV_REMOVED] &&
!Event._subs[EV_INSERTED] &&
!Event._subs[EV_CONTENT_CHANGE] &&
!Event._subs[EV_ATTRIBUTE_REMOVED] &&
!Event._subs[EV_ATTRIBUTE_CHANGED] &&
!Event._subs[EV_ATTRIBUTE_INSERTED]
) {
DOCUMENT.hasMutationSubs = false;
}
};
// Now a very tricky one:
// Some browsers do an array.sort down-top instead of top-down.
// In those cases we need another sortFn, for the position on an equal match should fall
// behind instead of before (which is the case on top-down sort)
[1,2].sort(function(a /*, b */) {
SORT || (SORT=(a===2) ? _sortFuncReversed : _sortFunc);
});
// Now we do some initialization in order to make DOM-events work:
Object.defineProperty(Event._defaultEventObj, 'currentTarget', {
get: function() {
var ev = this,
e_target = ev.target;
if (e_target && ev._s) {
if (e_target.matches(ev._s)) {
return e_target;
}
return e_target.inside(ev._s) || undefined;
}
}
});
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(UI+'*', _setupDomListener, Event)
._setEventObjProperty('stopPropagation', function() {this.status.ok || (this.status.propagationStopped = this.target);})
._setEventObjProperty('stopImmediatePropagation', function() {this.status.ok || (this.status.immediatePropagationStopped = this.target);});
// Notify when someone subscribes to any event at all --> we might need to transform the filterFn from a selector into a true fnction
// this is already done automaticly by _setupDomListener fo UI:* events
Event.notify('*:*', function(customEvent, subscriber) {
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0];
if ((emitterName!=='UI') && (typeof subscriber.f==='string')) {
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(customEvent, subscriber);
}
}, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(UI+'*', _teardownDomListener, Event);
Event._sellist = [_domSelToFunc];
_setupEvents();
// making HTMLElement to be able to emit using event-emitter:
(function(HTMLElementPrototype) {
HTMLElementPrototype.merge(Event.Emitter('UI'));
}(window.HTMLElement.prototype));
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(MUTATION_EVENTS, _setupMutationListener, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(MUTATION_EVENTS, _teardownMutationListener, Event);
// Note: window.document has no prototype
DOCUMENT.suppressMutationEvents = function(suppress) {
this._suppressMutationEvents = suppress;
};
// Event.noDeepDomEvt and Event._domCallback are the only method that is added to Event.
// We need to do this, because `event-mobile` needs access to the same method.
// We could have done without this method and instead listen for a custom-event to handle
// Mobile events, however, that would lead into 2 eventcycli which isn't performant.
/**
*
* @method noDeepDomEvt
* @param domEvent {String} the eventName that should be processed without deepsearch
* @param e {Object} eventobject
* @for Event
* @chainable
* @since 0.0.1
*/
Event.noDeepDomEvt = function(domEvent) {
domEvent.contains(':') || (domEvent=UI+domEvent);
NO_DEEP_SEARCH[domEvent] = true;
return this;
};
/**
* Does the actual transportation from DOM-events into the Eventsystem. It also looks at the response of
* the Eventsystem: on e.halt() or e.preventDefault(), the original DOM-event will be preventDefaulted.
*
* @method _domCallback
* @param eventName {String} the customEvent that is transported to the eventsystem
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
Event._domCallback = function(e) {
_evCallback(e);
};
// store module:
window._ITSAmodules.EventDom = Event;
return Event;
};
},{"event":3001,"js-ext/extra/hashmap.js":3017,"js-ext/lib/array.js":3018,"js-ext/lib/object.js":3019,"js-ext/lib/string.js":3020,"polyfill/polyfill-base.js":3032,"utils":3033,"vdom":3091}],2998:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":3003,"js-ext/lib/object.js":3004,"polyfill/polyfill-base.js":3016}],2999:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":2998}],3000:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":2998,"js-ext/extra/classes.js":3002,"js-ext/lib/object.js":3004}],3001:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":2998,"./event-emitter.js":2999,"./event-listener.js":3000}],3002:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":3004,"js-ext/extra/hashmap.js":3003,"polyfill/polyfill-base.js":3007}],3003:[function(require,module,exports){
module.exports=require(4)
},{}],3004:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3003,"polyfill/polyfill-base.js":3007,"utils":3008}],3005:[function(require,module,exports){
module.exports=require(14)
},{}],3006:[function(require,module,exports){
module.exports=require(15)
},{}],3007:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3005,"./lib/window.console.js":3006}],3008:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3009,"./lib/timers.js":3010}],3009:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3003,"polyfill/polyfill-base.js":3013}],3010:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3013}],3011:[function(require,module,exports){
module.exports=require(14)
},{}],3012:[function(require,module,exports){
module.exports=require(15)
},{}],3013:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3011,"./lib/window.console.js":3012}],3014:[function(require,module,exports){
module.exports=require(14)
},{}],3015:[function(require,module,exports){
module.exports=require(15)
},{}],3016:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3014,"./lib/window.console.js":3015}],3017:[function(require,module,exports){
module.exports=require(4)
},{}],3018:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":3023}],3019:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3017,"polyfill/polyfill-base.js":3023,"utils":3024}],3020:[function(require,module,exports){
module.exports=require(29)
},{}],3021:[function(require,module,exports){
module.exports=require(14)
},{}],3022:[function(require,module,exports){
module.exports=require(15)
},{}],3023:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3021,"./lib/window.console.js":3022}],3024:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3025,"./lib/timers.js":3026}],3025:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3017,"polyfill/polyfill-base.js":3029}],3026:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3029}],3027:[function(require,module,exports){
module.exports=require(14)
},{}],3028:[function(require,module,exports){
module.exports=require(15)
},{}],3029:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3027,"./lib/window.console.js":3028}],3030:[function(require,module,exports){
module.exports=require(14)
},{}],3031:[function(require,module,exports){
module.exports=require(15)
},{}],3032:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3030,"./lib/window.console.js":3031}],3033:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3034,"./lib/timers.js":3035}],3034:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3017,"polyfill/polyfill-base.js":3038}],3035:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3038}],3036:[function(require,module,exports){
module.exports=require(14)
},{}],3037:[function(require,module,exports){
module.exports=require(15)
},{}],3038:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3036,"./lib/window.console.js":3037}],3039:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],3040:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":3044,"js-ext/extra/hashmap.js":3041,"polyfill/polyfill-base.js":3050}],3041:[function(require,module,exports){
module.exports=require(4)
},{}],3042:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":3043,"../lib/object.js":3044,"./classes.js":3040,"js-ext/extra/hashmap.js":3041,"polyfill/lib/weakmap.js":3048}],3043:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":3050}],3044:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3041,"polyfill/polyfill-base.js":3050,"utils":3051}],3045:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":3050}],3046:[function(require,module,exports){
module.exports=require(29)
},{}],3047:[function(require,module,exports){
module.exports=require(14)
},{}],3048:[function(require,module,exports){
module.exports=require(57)
},{}],3049:[function(require,module,exports){
module.exports=require(15)
},{}],3050:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3047,"./lib/window.console.js":3049}],3051:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3052,"./lib/timers.js":3053}],3052:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3041,"polyfill/polyfill-base.js":3056}],3053:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3056}],3054:[function(require,module,exports){
module.exports=require(14)
},{}],3055:[function(require,module,exports){
module.exports=require(15)
},{}],3056:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3054,"./lib/window.console.js":3055}],3057:[function(require,module,exports){
module.exports=require(66)
},{}],3058:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":3057}],3059:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":3057}],3060:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":3057}],3061:[function(require,module,exports){
module.exports=require(14)
},{}],3062:[function(require,module,exports){
module.exports=require(15)
},{}],3063:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3061,"./lib/window.console.js":3062}],3064:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3065,"./lib/timers.js":3066}],3065:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3041,"polyfill/polyfill-base.js":3069}],3066:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3069}],3067:[function(require,module,exports){
module.exports=require(14)
},{}],3068:[function(require,module,exports){
module.exports=require(15)
},{}],3069:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3067,"./lib/window.console.js":3068}],3070:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":3071}],3071:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":3072,"js-ext/lib/object.js":3073}],3072:[function(require,module,exports){
module.exports=require(4)
},{}],3073:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3072,"polyfill/polyfill-base.js":3076,"utils":3077}],3074:[function(require,module,exports){
module.exports=require(14)
},{}],3075:[function(require,module,exports){
module.exports=require(15)
},{}],3076:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3074,"./lib/window.console.js":3075}],3077:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3078,"./lib/timers.js":3079}],3078:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3072,"polyfill/polyfill-base.js":3082}],3079:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3082}],3080:[function(require,module,exports){
module.exports=require(14)
},{}],3081:[function(require,module,exports){
module.exports=require(15)
},{}],3082:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3080,"./lib/window.console.js":3081}],3083:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":3041,"js-ext/lib/object.js":3044,"js-ext/lib/string.js":3046,"polyfill":3063,"polyfill/extra/transition.js":3058,"polyfill/extra/vendorCSS.js":3060}],3084:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":3041,"js-ext/lib/object.js":3044,"polyfill":3063}],3085:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":3041,"js-ext/lib/object.js":3044,"js-ext/lib/string.js":3046,"polyfill":3063}],3086:[function(require,module,exports){
arguments[4][95][0].apply(exports,arguments)
},{"../css/element.css":3039,"./attribute-extractor.js":3083,"./element-array.js":3084,"./html-parser.js":3087,"./node-parser.js":3088,"./vdom-ns.js":3089,"./vnode.js":3090,"js-ext/extra/hashmap.js":3041,"js-ext/lib/object.js":3044,"js-ext/lib/promise.js":3045,"js-ext/lib/string.js":3046,"polyfill":3063,"polyfill/extra/transition.js":3058,"polyfill/extra/transitionend.js":3059,"polyfill/extra/vendorCSS.js":3060,"utils":3064,"window-ext":3070}],3087:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":3083,"./vdom-ns.js":3089,"js-ext/extra/hashmap.js":3041,"js-ext/lib/object.js":3044,"polyfill":3063}],3088:[function(require,module,exports){
arguments[4][97][0].apply(exports,arguments)
},{"./attribute-extractor.js":3083,"./vdom-ns.js":3089,"./vnode.js":3090,"js-ext/extra/hashmap.js":3041,"js-ext/lib/object.js":3044,"polyfill":3063}],3089:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":3041,"js-ext/lib/object.js":3044,"polyfill":3063}],3090:[function(require,module,exports){
"use strict";
/**
* Delivers the `vnode` prototype object, which is a virtualisation of an `Element` inside the Dom.
* These Elements work smoothless with the vdom (see ...).
*
* vnodes are much quicker to access and walk through than native dom-nodes. However, this is a module you don't need
* by itself: `Element`-types use these features under the hood.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* <br>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
*
* @module vdom
* @submodule vnode
* @class vnode
* @since 0.0.1
*/
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('js-ext/lib/string.js');
require('polyfill');
var createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.VNode) {
return window._ITSAmodules.VNode; // VNODE was already created
}
console.warn('VDOM 24');
var NS = require('./vdom-ns.js')(window),
extractor = require('./attribute-extractor.js')(window),
DOCUMENT = window.document,
LightMap = require('js-ext/extra/lightmap.js'),
MUTATION_EVENTS = new LightMap(),
BATCH_WILL_RUN = false,
xmlNS = NS.xmlNS,
nodeids = NS.nodeids,
htmlToVNodes = require('./html-parser.js')(window),
timers = require('utils/lib/timers.js'),
async = timers.async,
later = timers.later,
/*jshint proto:true */
PROTO_SUPPORTED = !!Object.__proto__,
/*jshint proto:false */
// cleanup memory after 1 minute: removed nodes SHOULD NOT be accessed afterwards
// because vnode would be recalculated and might be different from before
DESTROY_DELAY = 60000,
unescapeEntities = NS.UnescapeEntities,
NTH_CHILD_REGEXP = /^(?:(\d*)[n|N])([\+|\-](\d+))?$/, // an+b
STRING = 'string',
CLASS = 'class',
STYLE = 'style',
ID = 'id',
NODE= 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
EV_REMOVED = NODE+REMOVE,
EV_INSERTED = NODE+INSERT,
EV_CONTENT_CHANGE = NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = ATTRIBUTE+INSERT,
SPLIT_CHARACTER = createHashMap({
' ': true,
'>': true,
'+': true, // only select the element when it is immediately preceded by the former element
'~': true // only the element when it has the former element as a sibling. (just like `+`, but less strict)
}),
STORABLE_SPLIT_CHARACTER = createHashMap({
'>': true,
'+': true,
'~': true
}),
SIBLING_MATCH_CHARACTER = createHashMap({
'+': true,
'~': true
}),
ATTR_DETAIL_SPECIFIERS = createHashMap({
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to attribute-name end-tokens.
*
* @property END_ATTRIBUTENAME
* @default {
* '=': true,
* ']': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
END_ATTRIBUTENAME = createHashMap({
'=': true,
']': true,
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to different changes of Element nodeType changes.
*
* @property NODESWITCH
* @default {
* 1: {
* 1: 1,
* 3: 2,
* 8: 3
* },
* 3: {
* 1: 4,
* 3: 5,
* 8: 6
* },
* 8: {
* 1: 7,
* 3: 8,
* 8: 9
* }
* }
* @type Object
* @protected
* @since 0.0.1
*/
NODESWITCH = createHashMap({
1: createHashMap({
1: 1, // oldNodeType==Element, newNodeType==Element
3: 2, // oldNodeType==Element, newNodeType==TextNode
8: 3 // oldNodeType==Element, newNodeType==Comment
}),
3: createHashMap({
1: 4, // oldNodeType==TextNode, newNodeType==Element
3: 5, // oldNodeType==TextNode, newNodeType==TextNode
8: 6 // oldNodeType==TextNode, newNodeType==Comment
}),
8: createHashMap({
1: 7, // oldNodeType==Comment, newNodeType==Element
3: 8, // oldNodeType==Comment, newNodeType==TextNode
8: 9 // oldNodeType==Comment, newNodeType==Comment
})
}),
/**
* Object to gain quick access to selector start-tokens.
*
* @property SELECTOR_IDENTIFIERS
* @default {
* '#': 1,
* '.': 2,
* '[': 3
* }
* @type Object
* @protected
* @since 0.0.1
*/
SELECTOR_IDENTIFIERS = createHashMap({
'#': 1,
'.': 2,
'[': 3,
':': 4
}),
PSEUDO_FIRST_CHILD = ':first-child',
PSEUDO_FIRST_OF_TYPE = ':first-of-type',
PSEUDO_LAST_CHILD = ':last-child',
PSEUDO_LAST_OF_TYPE = ':last-of-type',
PSEUDO_NTH_CHILD = ':nth-child',
PSEUDO_NTH_LAST_CHILD = ':nth-last-child',
PSEUDO_NTH_LAST_OF_TYPE = ':nth-last-of-type',
PSEUDO_NTH_OF_TYPE = ':nth-of-type',
PSEUDO_ONLY_OF_TYPE = ':only-of-type',
PSEUDO_ONLY_CHILD = ':only-child',
/**
* Object to gain quick access to the selectors that required children
*
* @property PSEUDO_REQUIRED_CHILDREN
* @default {
* ':first-child': true,
* ':first-of-type': true,
* ':last-child': true,
* ':last-of-type': true,
* ':nth-child': true,
* ':nth-last-child': true,
* ':nth-last-of-type': true,
* ':nth-of-type': true,
* ':only-of-type': true,
* ':only-child': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
PSEUDO_REQUIRED_CHILDREN = createHashMap(),
_matchesSelectorItem, _matchesOneSelector, _findElementSibling, vNodeProto, _markRemoved, _tryReplaceChild,
_splitSelector, _findNodeSibling, _matchNthChild, _batchEmit, _emitDestroyChildren, _tryRemoveDomNode;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_CHILD] = true;
/**
* Searches for the next -or previous- node-sibling (nodeType of 1, 3 or 8).
*
* @method _findNodeSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findNodeSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
index = vParent.vChildNodes.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildNodes[index];
};
/**
* Searches for the next -or previous- Element-sibling (nodeType of 1).
*
* @method _findElementSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findElementSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
if (vnode.nodeType===1) {
index = vParent.vChildren.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildren[index];
}
else {
/*jshint noempty:true */
while ((vnode=_findNodeSibling(vnode, next)) && (vnode.nodeType!==1)) {}
/*jshint noempty:false */
return vnode;
}
};
/**
* Check whether the vnode matches a "nth-child" test, which is used for css pseudoselectors like `nth-child`, `nth-of-type` etc.
*
* @method _matchNthChild
* @param pseudoArg {String} the argument for nth-child
* @param index {Number} the index of the inspected vnode
* @return {Boolean} whether the vnode matches the nthChild test
* @protected
* @private
* @since 0.0.1
*/
_matchNthChild = function(pseudoArg, index) {
var match, k, a, b, nodeOk, nthIndex, sign, isNumber;
(pseudoArg==='even') && (pseudoArg='2n');
(pseudoArg==='odd') && (pseudoArg='2n+1');
match = pseudoArg.match(NTH_CHILD_REGEXP) || (isNumber=pseudoArg.validateNumber());
if (!match) {
return false;
}
// pseudoArg follows the pattern: `an+b`
if (!isNumber) {
a = match[1];
sign = match[2];
b = match[3] || 0;
(b==='') && (b=0);
}
else {
b = pseudoArg;
}
sign && (sign=sign[0]);
if (!a) {
// only fixed index to match
return (sign==='-') ? false : (parseInt(b, 10)===index);
}
else {
// we need to iterate
nodeOk = false;
b = window.Number(b);
for (k=0; !nodeOk; k++) {
nthIndex = (sign==='-') ? (a*k) - b : (a*k) + b;
if (nthIndex===index) {
nodeOk = true;
}
else if (nthIndex>index) {
// beyond index --> will never become a fix anymore
return false;
}
}
return nodeOk;
}
};
/**
* Check whether the vnode matches the css-selector. the css-selector should be a single selector,
* not multiple, so it shouldn't contain a `comma`.
*
* @method _matchesOneSelector
* @param vnode {vnode} the vnode to inspect
* @param selector {String} the selector-item to check the match for
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches the css-selector
* @protected
* @private
* @since 0.0.1
*/
_matchesOneSelector = function(vnode, selector, relatedVNode) {
var selList = _splitSelector(selector),
size = selList.length,
selMatch = false,
i, selectorItem, last,
rightvnode, relationMatch, checkRelation;
if (STORABLE_SPLIT_CHARACTER[selList[size-1]]) {
return false;
}
relationMatch = function(leftVNode, rightVNode, relationCharacter) {
var match, vParent, vChildren;
// when `selector` starts with `>`, `~` or `+`, then
// there should also be a match comparing a related node!
switch (relationCharacter) {
case '>':
leftVNode || (leftVNode=rightVNode.vParent);
match = (leftVNode.vChildren.indexOf(rightVNode)!==-1);
break;
case '~':
leftVNode || (leftVNode=rightVNode.vFirstElement);
vParent = leftVNode.vParent;
vChildren = vParent && vParent.vChildren;
match = vParent && (vChildren.indexOf(leftVNode)<vChildren.indexOf(rightVNode));
break;
case '+':
leftVNode || (leftVNode=rightVNode.vPreviousElement);
match = (leftVNode.vNextElement === rightVNode);
}
return !!match;
};
last = size - 1;
i = last;
while (vnode && ((selMatch || (i===last)) && (i>=0))) {
selectorItem = selList[i];
if (STORABLE_SPLIT_CHARACTER[selectorItem]) {
checkRelation = selectorItem;
i--;
}
else {
selMatch = _matchesSelectorItem(vnode, selectorItem);
if (!selMatch && (i===last)) {
return false;
}
while (!selMatch && vnode && (i!==last)) {
// may also look at nodes higher in the chain
vnode = SIBLING_MATCH_CHARACTER[selList[i+1]] ? vnode.vPreviousElement : vnode.vParent;
selMatch = vnode && _matchesSelectorItem(vnode, selectorItem);
}
selMatch && checkRelation && vnode && (selMatch=relationMatch(vnode, rightvnode, checkRelation));
rightvnode = vnode;
if (vnode) {
// do a precheck on the next checkRelation:
// and determine whether to set the vnode upper the tree or at the previous sibling:
vnode = SIBLING_MATCH_CHARACTER[selList[i-1]] ? vnode.vPreviousElement : vnode.vParent;
}
if (selMatch) {
checkRelation = false;
i--;
}
}
}
if ((rightvnode===relatedVNode) || (i!==-1)) {
selMatch = false;
}
selMatch && checkRelation && rightvnode && (selMatch=relationMatch(relatedVNode, rightvnode, checkRelation));
return selMatch;
};
/**
* Check whether the vnode matches one specific selector-item. Suppose the css-selector: "#mynode li.red .blue"
* then there are 3 selector-items: "#mynode", "li.red" and ".blue"
*
* This method also can handle the new selectors:
* <ul>
* <li>[att^=val] –-> the “begins with” selector</li>
* <li>[att$=val] –-> the “ends with” selector</li>
* <li>[att*=val] –-> the “contains” selector (might be a substring)</li>
* <li>[att~=val] –-> the “contains” selector as a separate word, separated by spaces</li>
* <li>[att|=val] –-> the “contains” selector as a separate word, separated by `|`</li>
* <li>+ --> (same level)</li>
* <li>~ --> (same level)</li>
* </ul>
*
* @method _matchesSelectorItem
* @param vnode {Object} the vnode to inspect
* @param selectorItem {String} the selector-item to check the match for
* @return {Boolean} whether the vnode matches the selector-item
* @protected
* @private
* @since 0.0.1
*/
_matchesSelectorItem = function (vnode, selectorItem) {
var i = 0,
len = selectorItem.length,
character = selectorItem[0],
tagName, id, className, attributeName, attributeValue, stringMarker, attributeisString, isBoolean, insideAttributeValue, insideAttribute,
vParent, checkBoolean, treatment, k, min, max, value, len2, index, found, pseudo, pseudoArg, arglevel, count, vParentVChildren;
if (selectorItem==='*') {
return true;
}
if (!SELECTOR_IDENTIFIERS[character]) {
// starts with tagName
tagName = '';
// reposition i to continue in the right way:
i--;
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
tagName += character;
}
if (tagName.toUpperCase()!==vnode.tag) {
return false;
}
}
while (i<len) {
switch (character) {
case '#':
id = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
id += character;
}
if (id!==vnode.id) {
return false;
}
break;
case '.':
className = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
className += character;
}
if (!vnode.hasClass(className)) {
return false;
}
break;
case '[':
attributeName = '';
while ((++i<len) && (character=selectorItem[i]) && !END_ATTRIBUTENAME[character]) {
attributeName += character;
}
// if character===']' then we have an attribute without a value-definition
if (!vnode.attrs[attributeName] || ((character===']') && (vnode.attrs[attributeName]!==''))) {
return !!vnode.attrs[attributeName];
}
// now we read the value of the attribute
// however, it could be that the selector has a special `detailed` identifier set (defined by: ATTR_DETAIL_SPECIFIERS)
if (ATTR_DETAIL_SPECIFIERS[character]) {
treatment = character; // store the character to know how the attributedata should be treaded
i++; // character should be a "=" by now
}
else {
treatment = null;
}
attributeValue = '';
stringMarker = selectorItem[i+1];
attributeisString = (stringMarker==='"') || (stringMarker==="'");
attributeisString && (i++);
// end of attributaValue = (character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker))
while ((++i<len) && (character=selectorItem[i]) && !((character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker)))) {
attributeValue += character;
}
if (attributeisString) {
// if attribute is string, then we need to _remove to last stringmarker
attributeValue = attributeValue.substr(0, attributeValue.length-1);
}
else {
// if attribute is no string, then we need to typecast its value
isBoolean = ((attributeValue.length>3) && (attributeValue.length<6) &&
(checkBoolean=attributeValue.toUpperCase()) &&
((checkBoolean==='FALSE') || (checkBoolean==='TRUE')));
// typecast the value to either Boolean or Number:
attributeValue = isBoolean ? (checkBoolean==='TRUE') : parseFloat(attributeValue);
}
// depending upon how the attributedata should be treated:
if (treatment) {
switch (treatment) {
case '^': // “begins with” selector
if (!vnode.attrs[attributeName].startsWith(attributeValue)) {
return false;
}
break;
case '$': // “ends with” selector
if (!vnode.attrs[attributeName].endsWith(attributeValue)) {
return false;
}
break;
case '*': // “contains” selector (might be a substring)
if (!vnode.attrs[attributeName].contains(attributeValue)) {
return false;
}
break;
case '~': // “contains” selector as a separate word, separated by spaces
if (!(' '+vnode.attrs[attributeName]+' ').contains(' '+attributeValue+' ')) {
return false;
}
break;
case '|': // “contains” selector as a separate word, separated by `|`
if (!('|'+vnode.attrs[attributeName]+'|').contains('|'+attributeValue+'|')) {
return false;
}
break;
}
}
else if (vnode.attrs[attributeName]!==attributeValue) {
return false;
}
// we still need to increase one position:
(++i<len) && (character=selectorItem[i]);
break;
case ':':
// we have a pseudo-selector
// first, find out which one
// because '::' is a valid start (though without any selection), we start to back the next character as well:
pseudo = ':'+selectorItem[++i];
pseudoArg = '';
vParent = vnode.vParent;
vParentVChildren = vParent && vParent.vChildren;
// pseudo-selectors might have an argument passed in, like `:nth-child(2n+1)` or `:not([type="checkbox"])` --> we
// store this argument inside `pseudoArg`
// also note that combinations are possible with `:not` --> `:not(:nth-child(2n+1))`
// also note that we cannot "just" look for a closing character when running into the usage of attributes:
// for example --> `:not([data-x="some data :)"])`
// that's why -once we are inside attribute-data- we need to continue until the attribute-data ends
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
if (character==='(') {
// starting arguments
arglevel = 1;
insideAttribute = false;
insideAttributeValue = false;
while ((++i<len) && (character=selectorItem[i]) && (arglevel>0)) {
if (!insideAttribute) {
if (character==='(') {
arglevel++;
}
else if (character===')') {
arglevel--;
}
else if (character==='[') {
insideAttribute = true;
}
}
else {
// inside attribute
if (!insideAttributeValue) {
if ((character==='"') || (character==="'")) {
insideAttributeValue = true;
stringMarker = character;
}
else if (character===']') {
insideAttribute = false;
}
}
else if ((character===stringMarker) && (selectorItem[i+1]===']')) {
insideAttributeValue = false;
}
}
(arglevel>0) && (pseudoArg+=character);
}
}
else {
pseudo += character;
}
}
// now, `pseudo` is known as well as its possible pseudoArg
if (!vParentVChildren && PSEUDO_REQUIRED_CHILDREN[pseudo]) {
return false;
}
switch (pseudo) {
case ':checked': // input:checked Selects every checked <input> element
if (!vnode.attrs.checked) {
return false;
}
break;
case ':disabled': // input:disabled Selects every disabled <input> element
if (!vnode.attrs.disabled) {
return false;
}
break;
case ':empty': // p:empty Selects every <p> element that has no children (including text nodes)
if (vnode.vChildNodes && (vnode.vChildNodes.length>0)) {
return false;
}
break;
case ':enabled': // input:enabled Selects every enabled <input> element
if (vnode.attrs.disabled) {
return false;
}
break;
case PSEUDO_FIRST_CHILD: // p:first-child Selects every <p> element that is the first child of its parent
if (vParentVChildren[0]!==vnode) {
return false;
}
break;
case PSEUDO_FIRST_OF_TYPE: // p:first-of-type Selects every <p> element that is the first <p> element of its parent
for (k=vParentVChildren.indexOf(vnode)-1; k>=0; k--) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':focus': // input:focus Selects the input element which has focus
if (vnode.domNode!==DOCUMENT.activeElement) {
return false;
}
break;
case ':in-range': // input:in-range Selects input elements with a value within a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || (value<min) || (value>max)) {
return false;
}
break;
case ':lang': // p:lang(it) Selects every <p> element with a lang attribute equal to "it" (Italian)
if (vnode.attrs.lang!==pseudoArg) {
return false;
}
break;
case PSEUDO_LAST_CHILD: // p:last-child Selects every <p> element that is the last child of its parent
if (vParentVChildren[vParentVChildren.length-1]!==vnode) {
return false;
}
break;
case PSEUDO_LAST_OF_TYPE: // p:last-of-type Selects every <p> element that is the last <p> element of its parent
len2 = vParentVChildren.length;
for (k=vParentVChildren.indexOf(vnode)+1; k<len2; k++) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':not': // :not(p) Selects every element that is not a <p> element
if (vnode.matchesSelector(pseudoArg)) {
return false;
}
break;
case PSEUDO_NTH_CHILD: // p:nth-child(2) Selects every <p> element that is the second child of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
index = vParentVChildren.indexOf(vnode)+1;
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_CHILD: // p:nth-last-child(2) Selects every <p> element that is the second child of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_OF_TYPE: // p:nth-last-of-type(2) Selects every <p> element that is the second <p> element of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
index = 0;
for (k=vParentVChildren.length-1; (k>=0) && !found; k--) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_OF_TYPE: // p:nth-of-type(2) Selects every <p> element that is the second <p> element of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
len2 = vParentVChildren.length;
index = 0;
for (k=0; (k<len2) && !found; k++) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_ONLY_OF_TYPE: // p:only-of-type Selects every <p> element that is the only <p> element of its parent
len2 = vParentVChildren.length;
count = 0;
for (k=0; (k<len2) && (count<=1); k++) {
(vParentVChildren[k].tag===vnode.tag) && count++;
}
if (count!==1) {
return false;
}
break;
case PSEUDO_ONLY_CHILD: // p:only-child Selects every <p> element that is the only child of its parent
if (vParentVChildren.length!==1) {
return false;
}
break;
case ':optional': // input:optional Selects input elements with no "required" attribute
if (vnode.attrs.required) {
return false;
}
break;
case ':out-of-range': // input:out-of-range Selects input elements with a value outside a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || ((value>=min) && (value<=max))) {
return false;
}
break;
case ':read-only': // input:read-only Selects input elements with the "readonly" attribute specified
if (!vnode.attrs.readonly) {
return false;
}
break;
case ':read-write': // input:read-write Selects input elements with the "readonly" attribute NOT specified
if (vnode.attrs.readonly) {
return false;
}
break;
case ':required': // input:required Selects input elements with the "required" attribute specified
if (!vnode.attrs.required) {
return false;
}
break;
case ':root': // Selects the document's root element
if (vnode.domNode!==DOCUMENT.documentElement) {
return false;
}
break;
}
}
}
return true;
};
/**
* Splits the selector into separate subselector-items that should match different elements through the tree.
* Special characters '>' and '+' are added as separate items in the hash.
*
* @method _splitSelector
* @param selector {String} the selector-item to check the match for
* @return {Array} splitted selectors
* @protected
* @private
* @since 0.0.1
*/
_splitSelector = function(selector) {
var list = [],
len = selector.length,
sel = '',
i, character, insideDataAttr;
for (i=0; i<len; i++) {
character = selector[i];
if (character==='[') {
sel += character;
insideDataAttr = true;
}
else if (character===']') {
sel += character;
insideDataAttr = false;
}
else if (insideDataAttr || !SPLIT_CHARACTER[character]) {
sel += character;
}
else {
// unique selectoritem is found, add it to the list
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
// in case the last character was '>', '+' or '~', we need to add it as a separate item
STORABLE_SPLIT_CHARACTER[character] && (list[list.length]=character);
}
}
// add the last item
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
return list;
};
_batchEmit = function() {
// we will exactly define the 'UI:'-event --> Itags will have another emitterName
MUTATION_EVENTS.each(function (mutationEvents, vnode) {
var domNode = vnode.domNode;
if (mutationEvents[EV_REMOVED]) {
domNode.emit('UI:'+EV_REMOVED);
}
else if (mutationEvents[EV_INSERTED]) {
domNode.emit('UI:'+EV_INSERTED);
}
else {
// contentchange and attributechanges can go hand in hand
mutationEvents.each(function(value, evt) {
domNode.emit('UI:'+evt, (evt===EV_CONTENT_CHANGE) ? null : {changed: value});
});
}
});
MUTATION_EVENTS.clear();
BATCH_WILL_RUN = false;
};
_emitDestroyChildren = function(vnode) {
var children = vnode.vChildren,
len = children.length,
i, vChild;
for (i=0; i<len; i++) {
vChild = children[i];
vChild._emit(EV_REMOVED);
}
};
_markRemoved = function(vnode) {
var vChildNodes = vnode.vChildNodes,
len, i, vChildNode;
if (vnode.nodeType===1) {
Object.protectedProp(vnode, 'removedFromDOM', true);
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && _markRemoved(vChildNode);
}
}
}
};
/**
* A safe way to remove a dom-node, even if it is not present.
* Will not throw a JS error when the "to be removed" node isn't in the dom
*
* @method _tryRemoveDomNode
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param childDomNode {DOMNode} the node that needs to be removed
* @since 0.0.1
*/
_tryRemoveDomNode = function(parentDomNode, childDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && childDomNode.isItag && childDomNode.isItag()) {
DOCUMENT._itagList.remove(childDomNode);
}
try {
parentDomNode._removeChild(childDomNode);
}
catch(err) {}
};
/**
* A safe way to replace a dom-node, even if the "to be replaced"-node it is not present.
* Will not throw a JS error when the "to be replaced" node isn't in the dom. in that case, the new node will
* be appended
*
* @method _tryReplaceChild
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param newChildDomNode {DOMNode} the node that needs to be replaced
* @param oldChildDomNode {DOMNode} the node that needs to be inserted
* @since 0.0.1
*/
_tryReplaceChild = function(parentDomNode, newChildDomNode, oldChildDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && oldChildDomNode.isItag && oldChildDomNode.isItag()) {
DOCUMENT._itagList.remove(oldChildDomNode);
}
try {
parentDomNode._replaceChild(newChildDomNode, oldChildDomNode);
}
catch(err) {
// if the childDomNode isn;t there any more - for whatever reason - the we need to append the newChildNode
parentDomNode._appendChild(newChildDomNode);
}
};
vNodeProto = window._ITSAmodules.VNode = {
/**
* Check whether the vnode's domNode is equal, or contains the specified Element.
*
* @method contains
* @return {Boolean} whether the vnode's domNode is equal, or contains the specified Element.
* @since 0.0.1
*/
contains: function(otherVNode, noProtectedSearch) {
var instance = this;
if (otherVNode && (otherVNode.destroyed || (noProtectedSearch && otherVNode._systemNode))) {
return false;
}
while (otherVNode && (otherVNode!==instance)) {
otherVNode = otherVNode.vParent;
if (otherVNode && noProtectedSearch && (otherVNode._systemNode || (otherVNode.isItag && otherVNode.domNode.contentHidden))) {
return false;
}
}
return (otherVNode===instance);
},
/**
* Empties the vnode.
*
* Syncs with the dom.
*
* @method empty
* @param [full=false] {Boolean} whether system-nodes should be removed as well
* @chainable
* @since 0.0.1
*/
empty: function(full) {
delete this._scripts;
return this._setChildNodes([], null, full);
},
/**
* Returns the first child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method firstOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the first child-vnode or null when not present
* @since 0.0.1
*/
firstOfVChildren: function(cssSelector) {
var instance = this,
found, i, len, vChildren, element;
if (!cssSelector) {
return instance.vFirstElementChild;
}
vChildren = instance.vChildren;
len = vChildren.length;
for (i=0; !found && (i<len); i++) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
return found;
},
/**
* Gets the innerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* Only valid for nodetype=1 (HTMLElements)
*
* @method getHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String|undefined} the innerHTML without the elements specified, or `undefined` when not an HTMLElement
* @since 0.0.1
*/
getHTML: function(exclude, includeSystemNodes) {
var instance = this,
html, vChildNodes, len, i, vChildNode;
if (instance.nodeType===1) {
if (exclude) {
Array.isArray(exclude) || (exclude=[exclude]);
}
html = '';
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
switch (vChildNode.nodeType) {
case 1:
(exclude && exclude.contains(vChildNode.domNode)) || (!includeSystemNodes && vChildNode._systemNode) || (html+=vChildNode.getOuterHTML(exclude, includeSystemNodes));
break;
case 3:
html += vChildNode.text.replace(/</g, '<').replace(/>/g, '>');
break;
case 8:
html += '<!--' + vChildNode.text.replace(/</g, '<').replace(/>/g, '>') + '-->';
}
}
}
return html;
},
/**
* Gets the outerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* @method getOuterHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String} the outerHTML
* @since 0.0.1
*/
getOuterHTML: function(exclude, includeSystemNodes) {
var instance = this,
html,
attrs = instance.attrs;
if (instance.nodeType===1) {
if (instance.nodeType!==1) {
return instance.textContent;
}
html = '<' + instance.tag.toLowerCase();
attrs.each(function(value, key) {
html += ' '+key+'="'+value+'"';
});
instance.isVoid && (html += '/');
html += '>';
if (!instance.isVoid) {
html += ((!includeSystemNodes && instance.isItag) ? '' : instance.getHTML(exclude, includeSystemNodes)) + '</' + instance.tag.toLowerCase() + '>';
}
}
return html;
},
/**
* Checks whether the vnode has any vChildNodes (nodeType of 1, 3 or 8).
*
* @method hasVChildNodes
* @return {Boolean} whether the vnode has any vChildNodes.
* @since 0.0.1
*/
hasVChildNodes: function() {
return this.vChildNodes ? (this.vChildNodes.length>0) : false;
},
/**
* Checks whether the vnode has any vChildren (vChildNodes with nodeType of 1).
*
* @method hasVChildren
* @return {Boolean} whether the vnode has any vChildren.
* @since 0.0.1
*/
hasVChildren: function() {
return this.vChildNodes ? (this.vChildren.length>0) : false;
},
/**
* Checks whether the className is present on the vnode.
*
* @method hasClass
* @param className {String|Array} the className to check for. May be an Array of classNames, which all needs to be present.
* @return {Boolean} whether the className (or classNames) is present on the vnode
* @since 0.0.1
*/
hasClass: function(className) {
var instance = this,
check = function(cl) {
return !!instance.classNames[cl];
};
if (!instance.classNames) {
return false;
}
if (typeof className === STRING) {
return check(className);
}
else if (Array.isArray(className)) {
return className.every(check);
}
return false;
},
/**
* Returns the last child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method lastOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the last child-vnode or null when not present
* @since 0.0.1
*/
lastOfVChildren: function(cssSelector) {
var vChildren = this.vChildren,
found, i, element;
if (vChildren) {
if (!cssSelector) {
return this.vLastElementChild;
}
for (i=vChildren.length-1; !found && (i>=0); i--) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
}
return found;
},
/**
* Checks whether the vnode matches one of the specified selectors. `selectors` can be one, or multiple css-selectors,
* separated by a `comma`. For example: "#myid li.red blue" is one selector, "div.red, div.blue, div.green" are three selectors.
*
* @method matchesSelector
* @param selectors {String} one or more css-selectors
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches one of the selectors
* @since 0.0.1
*/
matchesSelector: function(selectors, relatedVNode) {
var instance = this;
if (instance.nodeType!==1) {
return false;
}
selectors = selectors.split(',');
// we can use Array.some, because there won't be many separated selectoritems,
// so the final invocation won't be delayed much compared to looping
return selectors.some(function(selector) {
return _matchesOneSelector(instance, selector, relatedVNode);
});
},
/**
* Reloads the DOM-attribute into the vnode.
*
* @method matchesSelector
* @param attributeName {String} the name of the attribute to be reloaded.
* @return {Node} the domNode that was reloaded.
* @since 0.0.1
*/
reloadAttr: function(attributeName) {
var instance = this,
domNode = instance.domNode,
attributeValue = domNode._getAttribute(attributeName),
attrs = instance.attrs,
extractStyle, extractClass;
if (instance.nodeType===1) {
attributeValue || (attributeValue='');
if (attributeValue==='') {
delete attrs[attributeName];
// in case of STYLE attributeName --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attributeName --> special treatment
(attributeName===CLASS) && (instance.classNames={});
// in case of ID attributeName --> special treatment
if ((attributeName===ID) && (instance.id)) {
delete nodeids[instance.id];
delete instance.id;
}
}
else {
attrs[attributeName] = attributeValue;
// in case of STYLE attributeName --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(attributeValue);
attributeValue = extractStyle.attrStyle;
if (attributeValue) {
attrs.style = attributeValue;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attributeName --> special treatment
extractClass = extractor.extractClass(attributeValue);
attributeValue = extractClass.attrClass;
if (attributeValue) {
attrs[CLASS] = attributeValue;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (instance.id!==attributeValue) && (delete nodeids[instance.id]);
instance.id = attributeValue;
nodeids[attributeValue] = domNode;
}
}
}
return domNode;
},
/**
* Returns the vnode's style in a serialized form: the way it appears in the dom.
*
* @method serializeStyles
* @return {String} vnode's style
* @since 0.0.1
*/
serializeStyles: function() {
return extractor.serializeStyles(this.styles);
},
/**
* Sets the vnode's and dom-nodes inner HTML.
*
* Syncs with the dom. Can be invoked multiple times without issues.
*
* @method setHTML
* @param content {String} the innerHTML
* @param [suppressItagRender] {Boolean} to suppress Itags from rendering
* @param [allowScripts=false] {Boolean} whether scripts are allowed --> these should be defined with `xscript` instead of `script`
* @chainable
* @since 0.0.1
*/
setHTML: function(content, suppressItagRender, allowScripts) {
var instance = this;
instance._setChildNodes(htmlToVNodes(content, vNodeProto, instance.ns, null, suppressItagRender, allowScripts), suppressItagRender);
return instance;
},
/**
* Syncs the vnode's nodeid (if available) inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom. Can be invoked multiple times without issues.
*
* @method storeId
* @chainable
* @since 0.0.1
*/
storeId: function() {
// store node/vnode inside WeakMap:
var instance = this;
instance.id ? (nodeids[instance.id]=instance.domNode) : (delete nodeids[instance.id]);
return instance;
},
//---- private ------------------------------------------------------------------
_addToTaglist: function() {
var instance = this,
itagList;
if (instance.isItag) {
itagList = DOCUMENT.getItags(); // also reads the dom if the list isn't build yet
instance._data || Object.protectedProp(instance, '_data', {});
if (!instance._data.ce_destroyed && !itagList.contains(instance.domNode)) {
itagList.push(instance.domNode);
}
}
},
/**
* Adds a vnode to the end of the list of vChildNodes.
*
* Syncs with the DOM.
*
* @method _appendChild
* @param VNode {vnode} vnode to append
* @private
* @return {Node} the Node that was appended
* @since 0.0.1
*/
_appendChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
size, noProccessScript, scriptContent;
if ((VNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (VNode.attrs && VNode.attrs.src) ? VNode.attrs.src : VNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
VNode._moveToParent(instance);
instance.domNode._appendChild(domNode);
if (VNode.nodeType===3) {
size = instance.vChildNodes.length;
instance._normalize();
// if the size changed, then the domNode was merged
(size===instance.vChildNodes.length) || (domNode=instance.vChildNodes[instance.vChildNodes.length-1].domNode);
}
if (VNode.nodeType===1) {
VNode._addToTaglist();
VNode._emit(EV_INSERTED);
}
return domNode;
}
},
/**
* Removes the vnode from its parent vChildNodes- and vChildren-list.
*
* Does NOT sync with the dom.
*
* @method _deleteFromParent
* @private
* @chainable
* @since 0.0.1
*/
_deleteFromParent: function() {
var instance = this,
vParent = instance.vParent;
if (vParent && vParent.vChildNodes) {
vParent.vChildNodes.remove(instance);
// force to recalculate the vChildren on a next call:
(instance.nodeType===1) && (vParent._vChildren=null);
}
return instance;
},
/**
* Cleans up (empties) vnode's `_data`
*
* @method _cleanData
* @private
* @chainable
* @since 0.0.1
*/
_cleanData: function() {
var instance = this,
data = instance._data;
data && data.each(
function(value, key) {
delete data[key];
}
);
return instance;
},
/**
* Cleans up (removes) duplicated `style` definitions.
*
* @method _cleanupStyle
* @private
* @chainable
* @since 0.0.1
*/
_cleanupStyle: function() {
var instance = this,
compare = [],
removal = [],
vChildren = instance.vChildren,
len = vChildren.length,
vChild, i, styleContent, vChildInner;
for (i=0; i<len; i++) {
vChild = vChildren[i];
if (vChild.tag==='STYLE') {
vChildInner = vChild.vChildNodes[0];
if (vChildInner) {
styleContent = vChildInner.text;
if (compare.contains(styleContent)) {
removal[removal.length] = vChild;
}
else {
compare[compare.length] = styleContent;
}
}
else {
// empty style-tag
removal[removal.length] = vChild;
}
}
}
removal.forEach(function(vnode) {
instance._removeChild(vnode);
});
},
/**
* Destroys the vnode and all its vnode-vChildNodes.
* Removes it from its vParent.vChildNodes list,
* also removes its definitions inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom.
*
* @method _destroy
* @private
* @chainable
* @since 0.0.1
*/
_destroy: function(silent) {
var instance = this,
vChildNodes = instance.vChildNodes,
len, i, vChildNode, vParent, treeNodes;
if (!instance.destroyed) {
if (!silent) {
// Because we don't want to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
later(function() {
instance._emit(EV_REMOVED, null, null, null, true);
}, 5);
}
Object.protectedProp(instance, 'destroyed', true);
// first: determine the dom-tree, which module `event-dom` needs to determine where the node was before it was destroyed:
treeNodes = [instance];
vParent = instance.vParent;
while (vParent) {
treeNodes[treeNodes.length] = vParent;
vParent = vParent.vParent;
}
// mark all its vChildNodes so we can see if the node is in the DOM
_markRemoved(instance);
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && instance.isItag) {
DOCUMENT._itagList.remove(instance.domNode);
}
instance._scripts && (instance._scripts.length=0);
// The definite cleanup needs to be done after a timeout:
// someone might need to handle the Element when removed (fe to cleanup specific things)
later(function() {
instance._cleanData();
if (instance.nodeType===1) {
// _destroy all its vChildNodes
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && vChildNode._destroy(true);
}
}
}
instance._vChildren = null;
// explicitely set instance.domNode._vnode and instance.domNode to null in order to prevent problems with the GC (we break the circular reference)
delete instance.domNode._vnode;
// if valid id, then _remove the DOMnodeRef from internal hash
instance.id && delete nodeids[instance.id];
}, silent ? 0 : DESTROY_DELAY);
instance._deleteFromParent();
// Do not make domNode `null` --> it could be used even when not in the dom
}
return instance;
},
_emit: function(evt, attribute, newValue, prevValue, destroyEvt) {
/**
* Emitted by every Element that gets inserted.
*
* @event nodeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets removed.
*
* @event noderemove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets its content changed (innerHTML/innerText).
*
* @event nodecontentchange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute inserted.
*
* @event attributeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* </ul>
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute removed.
*
* @event attributeremove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Strings of the attributeNames that are removed
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute changed.
*
* @event attributechange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* <li>prevValue</li>
* </ul>
* @since 0.1
*/
var instance = this,
silent, attrMutations, mutationEvents, mutation, vParent;
if (!DOCUMENT.hasMutationSubs || (instance.nodeType!==1)) {
return;
}
silent = !!DOCUMENT._suppressMutationEvents;
if (!silent && (destroyEvt || !instance.destroyed)) {
// Because we don't wannt to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
mutationEvents = MUTATION_EVENTS.get(instance) || {};
if (attribute) {
attrMutations = mutationEvents[evt] || [];
if (evt===EV_ATTRIBUTE_REMOVED) {
mutation = attribute;
}
else {
mutation = {
attribute: attribute
};
if ((evt===EV_ATTRIBUTE_INSERTED) || (evt===EV_ATTRIBUTE_CHANGED)) {
mutation.newValue = newValue;
}
if ((evt===EV_ATTRIBUTE_CHANGED) && prevValue) {
mutation.prevValue = prevValue;
}
}
attrMutations.push(mutation);
mutationEvents[evt] = attrMutations;
}
else {
mutationEvents[evt] = true;
}
MUTATION_EVENTS.set(instance, mutationEvents);
// now set all parent to have a nodecontentchange:
vParent = instance.vParent;
vParent && vParent._emit(EV_CONTENT_CHANGE);
// in case of removal we need to emit EV_REMOVED for all children right now
// for they will be actually removed silently after a delay of 1 minute
(evt===EV_REMOVED) && _emitDestroyChildren(instance);
if (!BATCH_WILL_RUN) {
BATCH_WILL_RUN = true;
async(function() {
_batchEmit();
});
}
}
return instance;
},
/**
* Inserts `newVNode` before `refVNode`.
*
* Syncs with the DOM.
*
* @method _insertBefore
* @param newVNode {vnode} vnode to insert
* @param refVNode {vnode} The vnode before which newVNode should be inserted.
* @private
* @return {Node} the Node being inserted (equals domNode)
* @since 0.0.1
*/
_insertBefore: function(newVNode, refVNode) {
var instance = this,
domNode = newVNode.domNode,
index = instance.vChildNodes.indexOf(refVNode),
noProccessScript, scriptContent;
if ((newVNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (newVNode.attrs && newVNode.attrs.src) ? newVNode.attrs.src : newVNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
if (index!==-1) {
newVNode._moveToParent(instance, index);
instance.domNode._insertBefore(domNode, refVNode.domNode);
(newVNode.nodeType===3) && instance._normalize();
if (newVNode.nodeType===1) {
newVNode._addToTaglist();
newVNode._emit(EV_INSERTED);
}
return domNode;
}
else {
console.warn('trying to insert before, but no ref-node found. Will append the new node');
return instance._appendChild(newVNode);
}
}
},
/**
* Moves the vnode from its current parent.vChildNodes list towards a new parent vnode at the specified position.
*
* Does NOT sync with the dom.
*
* @method _moveToParent
* @param parentVNode {vnode} the parent-vnode
* @param [index] {Number} the position of the child. When not specified, it will be appended.
* @private
* @chainable
* @since 0.0.1
*/
_moveToParent: function(parentVNode, index) {
var instance = this,
vParent = instance.vParent;
instance._deleteFromParent();
instance.vParent = parentVNode;
parentVNode.vChildNodes || (parentVNode.vChildNodes=[]);
(typeof index==='number') ? parentVNode.vChildNodes.insertAt(instance, index) : (parentVNode.vChildNodes[parentVNode.vChildNodes.length]=instance);
// force to recalculate the vChildren on a next call:
vParent && (instance.nodeType===1) && (vParent._vChildren = null);
// force to recalculate the vChildren on a next call:
parentVNode && (instance.nodeType===1) && (parentVNode._vChildren=null);
return instance;
},
/**
* Removes empty TextNodes and merges following TextNodes inside the vnode.
*
* Syncs with the dom.
*
* @method _normalize
* @private
* @chainable
* @since 0.0.1
*/
_normalize: function() {
var instance = this,
domNode = instance.domNode,
vChildNodes = instance.vChildNodes,
changed = false,
i, preChildNode, vChildNode;
if (!instance._unNormalizable && vChildNodes) {
for (i=vChildNodes.length-1; i>=0; i--) {
vChildNode = vChildNodes[i];
preChildNode = vChildNodes[i-1]; // i will get the value `-1` eventually, which leads into undefined preChildNode
if (vChildNode.nodeType===3) {
if (vChildNode.text==='') {
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
else if (preChildNode && preChildNode.nodeType===3) {
preChildNode.text += vChildNode.text;
preChildNode.domNode.nodeValue = unescapeEntities(preChildNode.text);
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
}
}
}
changed && instance._emit(EV_CONTENT_CHANGE);
return instance;
},
/**
* Makes the vnode `normalizable`. Could be set to `false` when batch-inserting nodes, while `normalizaing` manually at the end.
* Afterwards, you should always reset `normalizable` to true.
*
* @method _normalizable
* @param value {Boolean} whether the vnode should be normalisable.
* @private
* @chainable
* @since 0.0.1
*/
_normalizable: function(value) {
var instance = this;
value ? (delete instance._unNormalizable) : (instance._unNormalizable=true);
return instance;
},
/**
* Prevents MutationObserver from making the dom sync with the vnode.
* Should be used when manipulating the dom from within the vnode itself (to preventing looping)
*
* @method _noSync
* @chainable
* @private
* @since 0.0.1
*/
_noSync: function() {
var instance = this;
if (!instance._nosync) {
instance._nosync = true;
async(function() {
instance._nosync = false;
});
}
return instance;
},
/**
* Removes the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _removeAttr
* @param attributeName {String}
* @private
* @chainable
* @since 0.0.1
*/
_removeAttr: function(attributeName, suppressItagRender) {
var instance = this,
attributeNameSplitted, ns;
if (instance.isItag && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
console.warn('Not allowed to remove the attribute '+attributeName);
return instance;
}
if (instance.attrs[attributeName]!==undefined) {
delete instance.attrs[attributeName];
// in case of STYLE attribute --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attribute --> special treatment
(attributeName===CLASS) && (instance.classNames={});
if (attributeName===ID) {
delete nodeids[instance.id];
delete instance.id;
}
instance._emit(EV_ATTRIBUTE_REMOVED, attributeName);
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
instance.domNode._removeAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName);
}
else {
instance.domNode._removeAttribute(attributeName);
}
}
return instance;
},
/**
* Removes the vnode's child-vnode from its vChildren and the DOM.
*
* Syncs with the DOM.
*
* @method removeChild
* @param VNode {vnode} the child-vnode to remove
* @private
* @since 0.0.1
*/
_removeChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
hadFocus = domNode && domNode.hasFocus && domNode.hasFocus() && (VNode.attrs['fm-lastitem']==='true'),
parentVNode = VNode.vParent;
VNode._destroy();
_tryRemoveDomNode(instance.domNode, VNode.domNode);
instance._normalize();
// now, reset the focus on focusmanager when needed:
if (hadFocus) {
while (parentVNode && !parentVNode.attrs['fm-manage']) {
parentVNode = parentVNode.vParent;
}
parentVNode && parentVNode.domNode.focus();
}
},
/**
* Replaces the current vnode at the parent.vChildNode list by `newVNode`
*
* Does NOT sync with the dom.
*
* @method _replaceAtParent
* @param newVNode {Object} the new vnode which should take over the place of the current vnode
* @private
* @chainable
* @since 0.0.1
*/
_replaceAtParent: function(newVNode) {
var instance = this,
vParent = instance.vParent,
vChildNodes, index;
if (vParent && (vChildNodes=vParent.vChildNodes)) {
index = vChildNodes.indexOf(instance);
// force to recalculate the vChildren on a next call:
((instance.nodeType===1) || (newVNode.nodeType===1)) && (instance.vParent._vChildren=null);
vChildNodes[index] = newVNode;
}
return instance._destroy();
},
/**
* Sets the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _setAttr
* @param attributeName {String}
* @param value {String} the value for the attributeName
* @param [force=false] {Boolean} force the attribute to be set, even if restrictions would deny it
* @private
* @chainable
* @since 0.0.1
*/
_setAttr: function(attributeName, value, force, suppressItagRender) {
var instance = this,
extractStyle, extractClass,
attrs = instance.attrs,
prevVal = attrs[attributeName],
domNode = instance.domNode,
attributeNameSplitted, ns;
if (instance.isItag && !force && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
if (prevVal!=value) {
console.warn('Not allowed to set the attribute '+attributeName);
}
return instance;
}
// don't check by !== --> value isn't parsed into a String yet
if (prevVal && ((value===undefined) || (value===null))) {
instance._removeAttr(attributeName, suppressItagRender);
return instance;
}
// attribute-values are always Strings:
value = String(value);
// attribute-values will be stored without " or '
value = value.replace(/"/g, '"').replace(/'/g, "'");
if (prevVal!=value) {
attrs[attributeName] = value;
// in case of STYLE attribute --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(value);
value = extractStyle.attrStyle;
if (value) {
attrs.style = value;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attribute --> special treatment
extractClass = extractor.extractClass(value);
value = extractClass.attrClass;
if (value) {
attrs[CLASS] = value;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (delete nodeids[instance.id]);
instance.id = value;
nodeids[value] = domNode;
}
instance._emit(prevVal ? EV_ATTRIBUTE_CHANGED : EV_ATTRIBUTE_INSERTED, attributeName, value, prevVal);
// when set in the dom --> quotes need to be set as "
value = value.replace(/"/g, '"');
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
domNode._setAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName, value);
}
else {
domNode._setAttribute(attributeName, value);
}
}
return instance;
},
/**
* Redefines the attributes of both the vnode as well as its related dom-node. The new
* definition replaces any previous attributes (without touching unmodified attributes).
* the `is` attribute cannot be changed for itags.
*
* Syncs the new vnode's attributes with the dom.
*
* @method _setAttrs
* @param newAttrs {Object|Array} the new attributes to be set
* @private
* @chainable
* @since 0.0.1
*/
_setAttrs: function(newAttrs, suppressItagRender) {
// does sync the DOM
var instance = this,
attrsObj, attr, attrs, i, key, keys, len, value;
if (instance.nodeType!==1) {
return;
}
instance._noSync();
attrs = instance.attrs;
attrs.id && (delete nodeids[attrs.id]);
if (Object.isObject(newAttrs)) {
attrsObj = newAttrs;
}
else {
attrsObj = {};
len = newAttrs.length;
for (i=0; i<len; i++) {
attr = newAttrs[i];
attrsObj[attr.name] = attr.value;
}
}
if (instance.isItag && !suppressItagRender) {
if (attrs.is) {
attrsObj.is = attrs.is;
}
else {
delete attrsObj.is;
delete attrsObj.Is;
delete attrsObj.iS;
delete attrsObj.IS;
}
}
// first _remove the attributes that are no longer needed.
// quickest way for object iteration: http://jsperf.com/object-keys-iteration/20
keys = Object.keys(attrs);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
attrsObj[key] || instance._removeAttr(key, suppressItagRender);
}
// next: every attribute that differs: redefine
keys = Object.keys(attrsObj);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
value = attrsObj[key];
(attrs[key]===value) || instance._setAttr(key, value, suppressItagRender);
}
return instance;
},
/**
* Redefines the childNodes of both the vnode as well as its related dom-node. The new
* definition replaces any previous nodes. (without touching unmodified nodes).
*
* Syncs the new vnode's childNodes with the dom.
*
* @method _setChildNodes
* @param newVChildNodes {Array} array with vnodes which represent the new childNodes
* @private
* @chainable
* @since 0.0.1
*/
_setChildNodes: function(newVChildNodes, suppressItagRender, removeSystemElements) {
// does sync the DOM
var instance = this,
vChildNodes = instance.vChildNodes || [],
domNode = instance.domNode,
forRemoval = [],
i, oldChild, newChild, newLength, len, len2, childDomNode, nodeswitch, bkpAttrs, cleanupStyle,
process, bkpChildNodes, needNormalize, prevSuppress, scriptContent, _scripts, scriptLen, j;
instance._noSync();
// first: reset ._vChildren --> by making it empty, its getter will refresh its list on a next call
instance._vChildren = null;
// if newVChildNodes is undefined, then we assume it to be empty --> an empty array
newVChildNodes || (newVChildNodes=[]);
// quickest way to loop through array is by using for loops: http://jsperf.com/array-foreach-vs-for-loop/5
len = vChildNodes.length;
// we need to add the systemNodes -if any- to the new newVChildNodes: they should be retained.
// SystemNodes are always places at the beginning, so they won't get reshuffled:
if (!removeSystemElements) {
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
if (oldChild._systemNode) {
newVChildNodes.insertAt(oldChild, i);
}
else {
break;
}
}
}
newLength = newVChildNodes.length;
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
childDomNode = oldChild.domNode;
if (i < newLength) {
newChild = newVChildNodes[i];
newChild.vParent || (newChild.vParent=instance);
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
/*jshint boss:true */
switch (nodeswitch=NODESWITCH[oldChild.nodeType][newChild.nodeType]) {
/*jshint boss:false */
case 1: // oldNodeType==Element, newNodeType==Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
if ((oldChild.tag!==newChild.tag) ||
((oldChild.tag===newChild.tag) && oldChild.isItag && (oldChild.attrs.is!==newChild.attrs.is))) {
// new tag --> completely replace
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
/*jshint proto:true */
oldChild.isItag && oldChild.domNode.destroyUI && oldChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
newChild.attrs = {}; // reset to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
oldChild._replaceAtParent(newChild);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
else {
// same tag --> only update what is needed
// NOTE: when this._unchangableAttrs exists, an itag-element syncs its UI -->
if (oldChild._data) {
// we might need to set the class `itag-rendered` when the attributeData says so:
// this happens when an itag gets refreshed with an unrendered definition
if (oldChild._data.itagRendered && !newChild.hasClass('itag-rendered')) {
newChild.classNames['itag-rendered'] = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' '+'itag-rendered';
}
else {
newChild.attrs[CLASS] = 'itag-rendered';
}
}
// we might need to set the class `focussed` when the attributeData says so:
// this happens when an itag gets rerendered: its renderFn doesn't know if any elements
// were focussed
if (oldChild._data.focussed && !newChild.hasClass('focussed')) {
newChild.classNames.focussed = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' ' + 'focussed';
}
else {
newChild.attrs[CLASS] = 'focussed';
}
}
if (oldChild._data['fm-tabindex']) {
// node has the tabindex set by the focusmanager,
// but that info might got lost with re-rendering of the new element
newChild.attrs.tabindex = '0';
}
}
if (oldChild.isItag) {
prevSuppress = DOCUMENT._suppressMutationEvents || false;
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
/*jshint proto:true */
newChild.domNode.destroyUI && newChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
oldChild._setAttrs(newChild.attrs, suppressItagRender);
newChild._destroy(true); // destroy through the vnode and removing from DOCUMENT._itagList
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(prevSuppress);
}
else {
oldChild._setAttrs(newChild.attrs, suppressItagRender);
// next: sync the vChildNodes:
oldChild._setChildNodes(newChild.vChildNodes, suppressItagRender);
}
// reset ref. to the domNode, for it might have been changed by newChild:
oldChild.id && (nodeids[oldChild.id]=childDomNode);
newVChildNodes[i] = oldChild;
}
}
break;
case 2: // oldNodeType==Element, newNodeType==TextNode
// case2 and case3 should be treated the same
case 3: // oldNodeType==Element, newNodeType==Comment
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
oldChild._replaceAtParent(newChild);
instance._emit(EV_CONTENT_CHANGE);
break;
case 4: // oldNodeType==TextNode, newNodeType==Element
// case4 and case7 should be treated the same
case 7: // oldNodeType==Comment, newNodeType==Element
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
// oldChild.isVoid = newChild.isVoid;
// delete oldChild.text;
instance._emit(EV_CONTENT_CHANGE);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
break;
case 5: // oldNodeType==TextNode, newNodeType==TextNode
// case5 and case9 should be treated the same
case 9: // oldNodeType==Comment, newNodeType==Comment
if (oldChild.text!==newChild.text) {
oldChild.text = newChild.text;
oldChild.domNode.nodeValue = unescapeEntities(newChild.text);
instance._emit(EV_CONTENT_CHANGE);
}
newVChildNodes[i] = oldChild;
break;
case 6: // oldNodeType==TextNode, newNodeType==Comment
// case6 and case8 should be treated the same
case 8: // oldNodeType==Comment, newNodeType==TextNode
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = oldChild.vParent;
instance._emit(EV_CONTENT_CHANGE);
}
if ((nodeswitch===2) || (nodeswitch===5) || (nodeswitch===8)) {
needNormalize = true;
}
}
else {
// _remove previous definition
_tryRemoveDomNode(domNode, oldChild.domNode);
// the oldChild needs to be removed, however, this cannot be done right now, for it would effect the loop
// so we store it inside a hash to remove it later
forRemoval[forRemoval.length] = oldChild;
}
}
// now definitely remove marked childNodes:
len2 = forRemoval.length;
for (i=0; i<len2; i++) {
forRemoval[i]._destroy();
}
// now we add all new vChildNodes that go beyond `len`:
for (i = len; i < newLength; i++) {
newChild = newVChildNodes[i];
newChild.vParent = instance;
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
switch (newChild.nodeType) {
case 1: // Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
domNode._appendChild(newChild.domNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
break;
case 3: // TextNode
needNormalize = true;
// we need to break through --> no `break`
/* falls through */
default: // TextNode or CommentNode
// newChild.domNode.nodeValue = newChild.text;
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
domNode._appendChild(newChild.domNode);
instance._emit(EV_CONTENT_CHANGE);
}
newChild.storeId();
}
instance.vChildNodes = newVChildNodes;
needNormalize && instance._normalize();
cleanupStyle && instance._cleanupStyle();
return instance;
},
_setUnchangableAttrs: function(unchangableObj) {
this._unchangableAttrs = unchangableObj;
}
};
//---- properties ------------------------------------------------------------------
/**
* A hash of all the `attributes` of the vnode's representing dom-node.
*
* @property attrs
* @type Object
* @since 0.0.1
*/
/**
* Hash with all the classes of the vnode. Every class represents a key, all values are set `true`.
*
* @property classNames
* @type Object
* @since 0.0.1
*/
/**
* The `id` of the vnode's representing dom-node (if any).
*
* @property id
* @type String
* @since 0.0.1
*/
/**
* Tells whether tag is a void Element. Examples are: `br`, `img` and `input`. Non-void Elements are f.e. `div` and `table`.
* For TextNodes and CommentNodes, this property is `undefined`.
*
* @property isVoid
* @type Boolean
* @since 0.0.1
*/
/**
* The `nodeType` of the vnode's representing dom-node (1===ElementNode, 3===TextNode, 8===CommentNode).
*
* @property nodeType
* @type Number
* @since 0.0.1
*/
/**
* The `tag` of the vnode's representing dom-node (allways uppercase).
*
* @property tag
* @type String
* @since 0.0.1
*/
/**
* The `content` of the vnode's representing dom-node, in case it is a TextNode or CommentNode.
* Equals dom-node.nodeValue.
*
* Is `undefined` for ElementNodes.
*
* @property text
* @type String
* @since 0.0.1
*/
/**
* Hash with all the childNodes (vnodes). vChildNodes are any kind of vnodes (nodeType===1, 3 or 8)
*
* @property vChildNodes
* @type Array
* @since 0.0.1
*/
/**
* The underlying `dom-node` that the vnode represents.
*
* @property domNode
* @type domNode
* @since 0.0.1
*/
/**
* vnode's parentNode (defined as a vnode itself).
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
Object.defineProperties(vNodeProto, {
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property innerHTML
* @type String
* @since 0.0.1
*/
innerHTML: {
get: function() {
return this.getHTML();
},
set: function(v) {
this.setHTML(v);
}
},
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property nodeValue
* @type String
* @since 0.0.1
*/
nodeValue: {
get: function() {
var instance = this;
return ((instance.nodeType===3) || (instance.nodeType===8)) ? instance.text : null;
},
set: function(v) {
var instance = this,
newTextContent, prevTextContent;
if ((instance.nodeType===3) || (instance.nodeType===8)) {
prevTextContent = instance.domNode.textContent;
instance.domNode.textContent = v;
// set .text AFTER the dom-node is updated --> the content might be escaped!
newTextContent = instance.text = instance.domNode.textContent;
(newTextContent!==prevTextContent) && instance._emit(EV_CONTENT_CHANGE);
}
}
},
/**
* Gets or sets the outerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property outerHTML
* @type String
* @since 0.0.1
*/
outerHTML: {
get: function() {
return this.getOuterHTML();
},
set: function(v) {
var instance = this,
vParent = instance.vParent,
id = instance.attrs.id,
vnode, vnodes, bkpAttrs, bkpChildNodes, i, len, vChildNodes, isLastChildNode, index, refDomNode;
if ((instance.nodeType!==1) || !vParent) {
return;
}
instance._noSync();
vChildNodes = vParent.vChildNodes;
index = vChildNodes.indexOf(instance);
isLastChildNode = (index===(vChildNodes.length-1));
isLastChildNode || (refDomNode=vChildNodes[index+1].domNode);
vnodes = htmlToVNodes(v, vNodeProto, vParent.ns, vParent);
len = vnodes.length;
if (len>0) {
// the first vnode will replace the current instance:
vnode = vnodes[0];
if (vnode.nodeType===1) {
if (vnode.tag!==instance.tag) {
// new tag --> completely replace
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
id && (delete nodeids[id]);
vnode.attrs = {}; // reset to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
// vnode.attrs = bkpAttrs;
// vnode.vChildNodes = bkpChildNodes;
vnode.id && (nodeids[vnode.id]=vnode.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
else {
instance._setAttrs(vnode.attrs);
instance._setChildNodes(vnode.vChildNodes);
}
}
else {
id && (delete nodeids[id]);
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
}
for (i=1; i<len; i++) {
vnode = vnodes[i];
switch (vnode.nodeType) {
case 1: // Element
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
vnode.attrs = {}; // reset, to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._insertBefore(vnode.domNode, refDomNode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
break;
default: // TextNode or CommentNode
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._appendChild(vnode.domNode, refDomNode);
}
vnode.storeId();
vnode._moveToParent(vParent, index+i);
}
}
},
/**
* Gets or sets the innerContent of the Node as plain text.
*
* The setter syncs with the DOM.
*
* @property textContent
* @type String
* @since 0.0.1
*/
textContent: {
get: function() {
var instance = this,
text = '',
vChildNodes = instance.vChildNodes,
len, i, vChildNode;
if (instance.nodeType===1) {
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
text += (vChildNode.nodeType===3) ? vChildNode.text : ((vChildNode.nodeType===1) ? vChildNode.textContent : '');
}
}
else {
text = instance.text;
}
return text;
},
set: function(v) {
var vnode = Object.create(vNodeProto);
vnode.domNode = DOCUMENT.createTextNode(v);
// create circular reference:
vnode.domNode._vnode = vnode;
vnode.nodeType = 3;
vnode.text = vnode.domNode.textContent;
this._setChildNodes([vnode]);
}
},
/**
* Hash with all the children (vnodes). vChildren are vnodes that have a representing dom-node that is an HtmlElement (nodeType===1)
*
* @property vChildren
* @type Array
* @since 0.0.1
*/
vChildren: {
get: function() {
var instance = this,
children = instance._vChildren,
vChildNode, vChildNodes, i, len;
vChildNodes = instance.vChildNodes;
if (vChildNodes && !children) {
children = instance._vChildren = [];
len = vChildNodes.length;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
(vChildNode.nodeType===1) && (children[children.length]=vChildNode);
}
}
children || (children = instance._vChildren = []);
return children;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirst
* @type vnode
* @since 0.0.1
*/
vFirst: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstChild;
}
},
/**
* Reference to the first vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirstChild
* @type vnode
* @since 0.0.1
*/
vFirstChild: {
get: function() {
return (this.vChildNodes && this.vChildNodes[0]) || null;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vFirstElement
* @type vnode
* @since 0.0.1
*/
vFirstElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstElementChild;
}
},
/**
* Reference to the first vChild, where the related dom-node an Element (nodeType===1).
*
* @property vFirstElementChild
* @type vnode
* @since 0.0.1
*/
vFirstElementChild: {
get: function() {
return this.vChildren[0] || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLast
* @type vnode
* @since 0.0.1
*/
vLast: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastChild;
}
},
/**
* Reference to the last vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLastChild
* @type vnode
* @since 0.0.1
*/
vLastChild: {
get: function() {
var vChildNodes = this.vChildNodes;
return (vChildNodes && vChildNodes[vChildNodes.length-1]) || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vLastElement
* @type vnode
* @since 0.0.1
*/
vLastElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastElementChild;
}
},
/**
* Reference to the last vChild, where the related dom-node an Element (nodeType===1).
*
* @property vLastElementChild
* @type vnode
* @since 0.0.1
*/
vLastElementChild: {
get: function() {
var vChildren = this.vChildren;
return vChildren[vChildren.length-1] || null;
}
},
/**
* the Parent vnode
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
/**
* Reference to the next of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vNext
* @type vnode
* @since 0.0.1
*/
vNext: {
get: function() {
return _findNodeSibling(this, true);
}
},
/**
* Reference to the next of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vNextElement
* @type vnode
* @since 0.0.1
*/
vNextElement: {
get: function() {
return _findElementSibling(this, true);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vPrevious
* @type vnode
* @since 0.0.1
*/
vPrevious: {
get: function() {
return _findNodeSibling(this);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vPreviousElement
* @type vnode
* @since 0.0.1
*/
vPreviousElement: {
get: function() {
return _findElementSibling(this);
}
}
});
return vNodeProto;
};
},{"./attribute-extractor.js":3083,"./html-parser.js":3087,"./vdom-ns.js":3089,"js-ext/extra/hashmap.js":3041,"js-ext/extra/lightmap.js":3042,"js-ext/lib/array.js":3043,"js-ext/lib/object.js":3044,"js-ext/lib/string.js":3046,"polyfill":3063,"utils/lib/timers.js":3066}],3091:[function(require,module,exports){
arguments[4][100][0].apply(exports,arguments)
},{"./partials/extend-document.js":3085,"./partials/extend-element.js":3086,"./partials/node-parser.js":3088,"js-ext/extra/hashmap.js":3041,"js-ext/lib/object.js":3044,"utils/lib/timers.js":3066}],3092:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":3094,"js-ext/extra/hashmap.js":3093,"polyfill/polyfill-base.js":3099}],3093:[function(require,module,exports){
module.exports=require(4)
},{}],3094:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3093,"polyfill/polyfill-base.js":3099,"utils":3100}],3095:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":3099}],3096:[function(require,module,exports){
module.exports=require(29)
},{}],3097:[function(require,module,exports){
module.exports=require(14)
},{}],3098:[function(require,module,exports){
module.exports=require(15)
},{}],3099:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3097,"./lib/window.console.js":3098}],3100:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3101,"./lib/timers.js":3102}],3101:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3093,"polyfill/polyfill-base.js":3105}],3102:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3105}],3103:[function(require,module,exports){
module.exports=require(14)
},{}],3104:[function(require,module,exports){
module.exports=require(15)
},{}],3105:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3103,"./lib/window.console.js":3104}],3106:[function(require,module,exports){
module.exports=require(14)
},{}],3107:[function(require,module,exports){
module.exports=require(15)
},{}],3108:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3106,"./lib/window.console.js":3107}],3109:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3112}],3110:[function(require,module,exports){
module.exports=require(14)
},{}],3111:[function(require,module,exports){
module.exports=require(15)
},{}],3112:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3110,"./lib/window.console.js":3111}],3113:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],3114:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":3118,"js-ext/extra/hashmap.js":3115,"polyfill/polyfill-base.js":3124}],3115:[function(require,module,exports){
module.exports=require(4)
},{}],3116:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":3117,"../lib/object.js":3118,"./classes.js":3114,"js-ext/extra/hashmap.js":3115,"polyfill/lib/weakmap.js":3122}],3117:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":3124}],3118:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3115,"polyfill/polyfill-base.js":3124,"utils":3125}],3119:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":3124}],3120:[function(require,module,exports){
module.exports=require(29)
},{}],3121:[function(require,module,exports){
module.exports=require(14)
},{}],3122:[function(require,module,exports){
module.exports=require(57)
},{}],3123:[function(require,module,exports){
module.exports=require(15)
},{}],3124:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3121,"./lib/window.console.js":3123}],3125:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3126,"./lib/timers.js":3127}],3126:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3115,"polyfill/polyfill-base.js":3130}],3127:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3130}],3128:[function(require,module,exports){
module.exports=require(14)
},{}],3129:[function(require,module,exports){
module.exports=require(15)
},{}],3130:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3128,"./lib/window.console.js":3129}],3131:[function(require,module,exports){
module.exports=require(66)
},{}],3132:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":3131}],3133:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":3131}],3134:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":3131}],3135:[function(require,module,exports){
module.exports=require(14)
},{}],3136:[function(require,module,exports){
module.exports=require(15)
},{}],3137:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3135,"./lib/window.console.js":3136}],3138:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3139,"./lib/timers.js":3140}],3139:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3115,"polyfill/polyfill-base.js":3143}],3140:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3143}],3141:[function(require,module,exports){
module.exports=require(14)
},{}],3142:[function(require,module,exports){
module.exports=require(15)
},{}],3143:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3141,"./lib/window.console.js":3142}],3144:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":3145}],3145:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":3146,"js-ext/lib/object.js":3147}],3146:[function(require,module,exports){
module.exports=require(4)
},{}],3147:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3146,"polyfill/polyfill-base.js":3150,"utils":3151}],3148:[function(require,module,exports){
module.exports=require(14)
},{}],3149:[function(require,module,exports){
module.exports=require(15)
},{}],3150:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3148,"./lib/window.console.js":3149}],3151:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3152,"./lib/timers.js":3153}],3152:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3146,"polyfill/polyfill-base.js":3156}],3153:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3156}],3154:[function(require,module,exports){
module.exports=require(14)
},{}],3155:[function(require,module,exports){
module.exports=require(15)
},{}],3156:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3154,"./lib/window.console.js":3155}],3157:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":3115,"js-ext/lib/object.js":3118,"js-ext/lib/string.js":3120,"polyfill":3137,"polyfill/extra/transition.js":3132,"polyfill/extra/vendorCSS.js":3134}],3158:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":3115,"js-ext/lib/object.js":3118,"polyfill":3137}],3159:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":3115,"js-ext/lib/object.js":3118,"js-ext/lib/string.js":3120,"polyfill":3137}],3160:[function(require,module,exports){
arguments[4][95][0].apply(exports,arguments)
},{"../css/element.css":3113,"./attribute-extractor.js":3157,"./element-array.js":3158,"./html-parser.js":3161,"./node-parser.js":3162,"./vdom-ns.js":3163,"./vnode.js":3164,"js-ext/extra/hashmap.js":3115,"js-ext/lib/object.js":3118,"js-ext/lib/promise.js":3119,"js-ext/lib/string.js":3120,"polyfill":3137,"polyfill/extra/transition.js":3132,"polyfill/extra/transitionend.js":3133,"polyfill/extra/vendorCSS.js":3134,"utils":3138,"window-ext":3144}],3161:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":3157,"./vdom-ns.js":3163,"js-ext/extra/hashmap.js":3115,"js-ext/lib/object.js":3118,"polyfill":3137}],3162:[function(require,module,exports){
arguments[4][97][0].apply(exports,arguments)
},{"./attribute-extractor.js":3157,"./vdom-ns.js":3163,"./vnode.js":3164,"js-ext/extra/hashmap.js":3115,"js-ext/lib/object.js":3118,"polyfill":3137}],3163:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":3115,"js-ext/lib/object.js":3118,"polyfill":3137}],3164:[function(require,module,exports){
"use strict";
/**
* Delivers the `vnode` prototype object, which is a virtualisation of an `Element` inside the Dom.
* These Elements work smoothless with the vdom (see ...).
*
* vnodes are much quicker to access and walk through than native dom-nodes. However, this is a module you don't need
* by itself: `Element`-types use these features under the hood.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* <br>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
*
* @module vdom
* @submodule vnode
* @class vnode
* @since 0.0.1
*/
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('js-ext/lib/string.js');
require('polyfill');
var createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.VNode) {
return window._ITSAmodules.VNode; // VNODE was already created
}
console.warn('VDOM 23');
var NS = require('./vdom-ns.js')(window),
extractor = require('./attribute-extractor.js')(window),
DOCUMENT = window.document,
LightMap = require('js-ext/extra/lightmap.js'),
MUTATION_EVENTS = new LightMap(),
BATCH_WILL_RUN = false,
xmlNS = NS.xmlNS,
nodeids = NS.nodeids,
htmlToVNodes = require('./html-parser.js')(window),
timers = require('utils/lib/timers.js'),
async = timers.async,
later = timers.later,
/*jshint proto:true */
PROTO_SUPPORTED = !!Object.__proto__,
/*jshint proto:false */
// cleanup memory after 1 minute: removed nodes SHOULD NOT be accessed afterwards
// because vnode would be recalculated and might be different from before
DESTROY_DELAY = 60000,
unescapeEntities = NS.UnescapeEntities,
NTH_CHILD_REGEXP = /^(?:(\d*)[n|N])([\+|\-](\d+))?$/, // an+b
STRING = 'string',
CLASS = 'class',
STYLE = 'style',
ID = 'id',
NODE= 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
EV_REMOVED = NODE+REMOVE,
EV_INSERTED = NODE+INSERT,
EV_CONTENT_CHANGE = NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = ATTRIBUTE+INSERT,
SPLIT_CHARACTER = createHashMap({
' ': true,
'>': true,
'+': true, // only select the element when it is immediately preceded by the former element
'~': true // only the element when it has the former element as a sibling. (just like `+`, but less strict)
}),
STORABLE_SPLIT_CHARACTER = createHashMap({
'>': true,
'+': true,
'~': true
}),
SIBLING_MATCH_CHARACTER = createHashMap({
'+': true,
'~': true
}),
ATTR_DETAIL_SPECIFIERS = createHashMap({
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to attribute-name end-tokens.
*
* @property END_ATTRIBUTENAME
* @default {
* '=': true,
* ']': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
END_ATTRIBUTENAME = createHashMap({
'=': true,
']': true,
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to different changes of Element nodeType changes.
*
* @property NODESWITCH
* @default {
* 1: {
* 1: 1,
* 3: 2,
* 8: 3
* },
* 3: {
* 1: 4,
* 3: 5,
* 8: 6
* },
* 8: {
* 1: 7,
* 3: 8,
* 8: 9
* }
* }
* @type Object
* @protected
* @since 0.0.1
*/
NODESWITCH = createHashMap({
1: createHashMap({
1: 1, // oldNodeType==Element, newNodeType==Element
3: 2, // oldNodeType==Element, newNodeType==TextNode
8: 3 // oldNodeType==Element, newNodeType==Comment
}),
3: createHashMap({
1: 4, // oldNodeType==TextNode, newNodeType==Element
3: 5, // oldNodeType==TextNode, newNodeType==TextNode
8: 6 // oldNodeType==TextNode, newNodeType==Comment
}),
8: createHashMap({
1: 7, // oldNodeType==Comment, newNodeType==Element
3: 8, // oldNodeType==Comment, newNodeType==TextNode
8: 9 // oldNodeType==Comment, newNodeType==Comment
})
}),
/**
* Object to gain quick access to selector start-tokens.
*
* @property SELECTOR_IDENTIFIERS
* @default {
* '#': 1,
* '.': 2,
* '[': 3
* }
* @type Object
* @protected
* @since 0.0.1
*/
SELECTOR_IDENTIFIERS = createHashMap({
'#': 1,
'.': 2,
'[': 3,
':': 4
}),
PSEUDO_FIRST_CHILD = ':first-child',
PSEUDO_FIRST_OF_TYPE = ':first-of-type',
PSEUDO_LAST_CHILD = ':last-child',
PSEUDO_LAST_OF_TYPE = ':last-of-type',
PSEUDO_NTH_CHILD = ':nth-child',
PSEUDO_NTH_LAST_CHILD = ':nth-last-child',
PSEUDO_NTH_LAST_OF_TYPE = ':nth-last-of-type',
PSEUDO_NTH_OF_TYPE = ':nth-of-type',
PSEUDO_ONLY_OF_TYPE = ':only-of-type',
PSEUDO_ONLY_CHILD = ':only-child',
/**
* Object to gain quick access to the selectors that required children
*
* @property PSEUDO_REQUIRED_CHILDREN
* @default {
* ':first-child': true,
* ':first-of-type': true,
* ':last-child': true,
* ':last-of-type': true,
* ':nth-child': true,
* ':nth-last-child': true,
* ':nth-last-of-type': true,
* ':nth-of-type': true,
* ':only-of-type': true,
* ':only-child': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
PSEUDO_REQUIRED_CHILDREN = createHashMap(),
_matchesSelectorItem, _matchesOneSelector, _findElementSibling, vNodeProto, _markRemoved, _tryReplaceChild,
_splitSelector, _findNodeSibling, _matchNthChild, _batchEmit, _emitDestroyChildren, _tryRemoveDomNode;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_CHILD] = true;
/**
* Searches for the next -or previous- node-sibling (nodeType of 1, 3 or 8).
*
* @method _findNodeSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findNodeSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
index = vParent.vChildNodes.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildNodes[index];
};
/**
* Searches for the next -or previous- Element-sibling (nodeType of 1).
*
* @method _findElementSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findElementSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
if (vnode.nodeType===1) {
index = vParent.vChildren.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildren[index];
}
else {
/*jshint noempty:true */
while ((vnode=_findNodeSibling(vnode, next)) && (vnode.nodeType!==1)) {}
/*jshint noempty:false */
return vnode;
}
};
/**
* Check whether the vnode matches a "nth-child" test, which is used for css pseudoselectors like `nth-child`, `nth-of-type` etc.
*
* @method _matchNthChild
* @param pseudoArg {String} the argument for nth-child
* @param index {Number} the index of the inspected vnode
* @return {Boolean} whether the vnode matches the nthChild test
* @protected
* @private
* @since 0.0.1
*/
_matchNthChild = function(pseudoArg, index) {
var match, k, a, b, nodeOk, nthIndex, sign, isNumber;
(pseudoArg==='even') && (pseudoArg='2n');
(pseudoArg==='odd') && (pseudoArg='2n+1');
match = pseudoArg.match(NTH_CHILD_REGEXP) || (isNumber=pseudoArg.validateNumber());
if (!match) {
return false;
}
// pseudoArg follows the pattern: `an+b`
if (!isNumber) {
a = match[1];
sign = match[2];
b = match[3] || 0;
(b==='') && (b=0);
}
else {
b = pseudoArg;
}
sign && (sign=sign[0]);
if (!a) {
// only fixed index to match
return (sign==='-') ? false : (parseInt(b, 10)===index);
}
else {
// we need to iterate
nodeOk = false;
b = window.Number(b);
for (k=0; !nodeOk; k++) {
nthIndex = (sign==='-') ? (a*k) - b : (a*k) + b;
if (nthIndex===index) {
nodeOk = true;
}
else if (nthIndex>index) {
// beyond index --> will never become a fix anymore
return false;
}
}
return nodeOk;
}
};
/**
* Check whether the vnode matches the css-selector. the css-selector should be a single selector,
* not multiple, so it shouldn't contain a `comma`.
*
* @method _matchesOneSelector
* @param vnode {vnode} the vnode to inspect
* @param selector {String} the selector-item to check the match for
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches the css-selector
* @protected
* @private
* @since 0.0.1
*/
_matchesOneSelector = function(vnode, selector, relatedVNode) {
var selList = _splitSelector(selector),
size = selList.length,
selMatch = false,
i, selectorItem, last,
rightvnode, relationMatch, checkRelation;
if (STORABLE_SPLIT_CHARACTER[selList[size-1]]) {
return false;
}
relationMatch = function(leftVNode, rightVNode, relationCharacter) {
var match, vParent, vChildren;
// when `selector` starts with `>`, `~` or `+`, then
// there should also be a match comparing a related node!
switch (relationCharacter) {
case '>':
leftVNode || (leftVNode=rightVNode.vParent);
match = (leftVNode.vChildren.indexOf(rightVNode)!==-1);
break;
case '~':
leftVNode || (leftVNode=rightVNode.vFirstElement);
vParent = leftVNode.vParent;
vChildren = vParent && vParent.vChildren;
match = vParent && (vChildren.indexOf(leftVNode)<vChildren.indexOf(rightVNode));
break;
case '+':
leftVNode || (leftVNode=rightVNode.vPreviousElement);
match = (leftVNode.vNextElement === rightVNode);
}
return !!match;
};
last = size - 1;
i = last;
while (vnode && ((selMatch || (i===last)) && (i>=0))) {
selectorItem = selList[i];
if (STORABLE_SPLIT_CHARACTER[selectorItem]) {
checkRelation = selectorItem;
i--;
}
else {
selMatch = _matchesSelectorItem(vnode, selectorItem);
if (!selMatch && (i===last)) {
return false;
}
while (!selMatch && vnode && (i!==last)) {
// may also look at nodes higher in the chain
vnode = SIBLING_MATCH_CHARACTER[selList[i+1]] ? vnode.vPreviousElement : vnode.vParent;
selMatch = vnode && _matchesSelectorItem(vnode, selectorItem);
}
selMatch && checkRelation && vnode && (selMatch=relationMatch(vnode, rightvnode, checkRelation));
rightvnode = vnode;
if (vnode) {
// do a precheck on the next checkRelation:
// and determine whether to set the vnode upper the tree or at the previous sibling:
vnode = SIBLING_MATCH_CHARACTER[selList[i-1]] ? vnode.vPreviousElement : vnode.vParent;
}
if (selMatch) {
checkRelation = false;
i--;
}
}
}
if ((rightvnode===relatedVNode) || (i!==-1)) {
selMatch = false;
}
selMatch && checkRelation && rightvnode && (selMatch=relationMatch(relatedVNode, rightvnode, checkRelation));
return selMatch;
};
/**
* Check whether the vnode matches one specific selector-item. Suppose the css-selector: "#mynode li.red .blue"
* then there are 3 selector-items: "#mynode", "li.red" and ".blue"
*
* This method also can handle the new selectors:
* <ul>
* <li>[att^=val] –-> the “begins with” selector</li>
* <li>[att$=val] –-> the “ends with” selector</li>
* <li>[att*=val] –-> the “contains” selector (might be a substring)</li>
* <li>[att~=val] –-> the “contains” selector as a separate word, separated by spaces</li>
* <li>[att|=val] –-> the “contains” selector as a separate word, separated by `|`</li>
* <li>+ --> (same level)</li>
* <li>~ --> (same level)</li>
* </ul>
*
* @method _matchesSelectorItem
* @param vnode {Object} the vnode to inspect
* @param selectorItem {String} the selector-item to check the match for
* @return {Boolean} whether the vnode matches the selector-item
* @protected
* @private
* @since 0.0.1
*/
_matchesSelectorItem = function (vnode, selectorItem) {
var i = 0,
len = selectorItem.length,
character = selectorItem[0],
tagName, id, className, attributeName, attributeValue, stringMarker, attributeisString, isBoolean, insideAttributeValue, insideAttribute,
vParent, checkBoolean, treatment, k, min, max, value, len2, index, found, pseudo, pseudoArg, arglevel, count, vParentVChildren;
if (selectorItem==='*') {
return true;
}
if (!SELECTOR_IDENTIFIERS[character]) {
// starts with tagName
tagName = '';
// reposition i to continue in the right way:
i--;
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
tagName += character;
}
if (tagName.toUpperCase()!==vnode.tag) {
return false;
}
}
while (i<len) {
switch (character) {
case '#':
id = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
id += character;
}
if (id!==vnode.id) {
return false;
}
break;
case '.':
className = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
className += character;
}
if (!vnode.hasClass(className)) {
return false;
}
break;
case '[':
attributeName = '';
while ((++i<len) && (character=selectorItem[i]) && !END_ATTRIBUTENAME[character]) {
attributeName += character;
}
// if character===']' then we have an attribute without a value-definition
if (!vnode.attrs[attributeName] || ((character===']') && (vnode.attrs[attributeName]!==''))) {
return !!vnode.attrs[attributeName];
}
// now we read the value of the attribute
// however, it could be that the selector has a special `detailed` identifier set (defined by: ATTR_DETAIL_SPECIFIERS)
if (ATTR_DETAIL_SPECIFIERS[character]) {
treatment = character; // store the character to know how the attributedata should be treaded
i++; // character should be a "=" by now
}
else {
treatment = null;
}
attributeValue = '';
stringMarker = selectorItem[i+1];
attributeisString = (stringMarker==='"') || (stringMarker==="'");
attributeisString && (i++);
// end of attributaValue = (character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker))
while ((++i<len) && (character=selectorItem[i]) && !((character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker)))) {
attributeValue += character;
}
if (attributeisString) {
// if attribute is string, then we need to _remove to last stringmarker
attributeValue = attributeValue.substr(0, attributeValue.length-1);
}
else {
// if attribute is no string, then we need to typecast its value
isBoolean = ((attributeValue.length>3) && (attributeValue.length<6) &&
(checkBoolean=attributeValue.toUpperCase()) &&
((checkBoolean==='FALSE') || (checkBoolean==='TRUE')));
// typecast the value to either Boolean or Number:
attributeValue = isBoolean ? (checkBoolean==='TRUE') : parseFloat(attributeValue);
}
// depending upon how the attributedata should be treated:
if (treatment) {
switch (treatment) {
case '^': // “begins with” selector
if (!vnode.attrs[attributeName].startsWith(attributeValue)) {
return false;
}
break;
case '$': // “ends with” selector
if (!vnode.attrs[attributeName].endsWith(attributeValue)) {
return false;
}
break;
case '*': // “contains” selector (might be a substring)
if (!vnode.attrs[attributeName].contains(attributeValue)) {
return false;
}
break;
case '~': // “contains” selector as a separate word, separated by spaces
if (!(' '+vnode.attrs[attributeName]+' ').contains(' '+attributeValue+' ')) {
return false;
}
break;
case '|': // “contains” selector as a separate word, separated by `|`
if (!('|'+vnode.attrs[attributeName]+'|').contains('|'+attributeValue+'|')) {
return false;
}
break;
}
}
else if (vnode.attrs[attributeName]!==attributeValue) {
return false;
}
// we still need to increase one position:
(++i<len) && (character=selectorItem[i]);
break;
case ':':
// we have a pseudo-selector
// first, find out which one
// because '::' is a valid start (though without any selection), we start to back the next character as well:
pseudo = ':'+selectorItem[++i];
pseudoArg = '';
vParent = vnode.vParent;
vParentVChildren = vParent && vParent.vChildren;
// pseudo-selectors might have an argument passed in, like `:nth-child(2n+1)` or `:not([type="checkbox"])` --> we
// store this argument inside `pseudoArg`
// also note that combinations are possible with `:not` --> `:not(:nth-child(2n+1))`
// also note that we cannot "just" look for a closing character when running into the usage of attributes:
// for example --> `:not([data-x="some data :)"])`
// that's why -once we are inside attribute-data- we need to continue until the attribute-data ends
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
if (character==='(') {
// starting arguments
arglevel = 1;
insideAttribute = false;
insideAttributeValue = false;
while ((++i<len) && (character=selectorItem[i]) && (arglevel>0)) {
if (!insideAttribute) {
if (character==='(') {
arglevel++;
}
else if (character===')') {
arglevel--;
}
else if (character==='[') {
insideAttribute = true;
}
}
else {
// inside attribute
if (!insideAttributeValue) {
if ((character==='"') || (character==="'")) {
insideAttributeValue = true;
stringMarker = character;
}
else if (character===']') {
insideAttribute = false;
}
}
else if ((character===stringMarker) && (selectorItem[i+1]===']')) {
insideAttributeValue = false;
}
}
(arglevel>0) && (pseudoArg+=character);
}
}
else {
pseudo += character;
}
}
// now, `pseudo` is known as well as its possible pseudoArg
if (!vParentVChildren && PSEUDO_REQUIRED_CHILDREN[pseudo]) {
return false;
}
switch (pseudo) {
case ':checked': // input:checked Selects every checked <input> element
if (!vnode.attrs.checked) {
return false;
}
break;
case ':disabled': // input:disabled Selects every disabled <input> element
if (!vnode.attrs.disabled) {
return false;
}
break;
case ':empty': // p:empty Selects every <p> element that has no children (including text nodes)
if (vnode.vChildNodes && (vnode.vChildNodes.length>0)) {
return false;
}
break;
case ':enabled': // input:enabled Selects every enabled <input> element
if (vnode.attrs.disabled) {
return false;
}
break;
case PSEUDO_FIRST_CHILD: // p:first-child Selects every <p> element that is the first child of its parent
if (vParentVChildren[0]!==vnode) {
return false;
}
break;
case PSEUDO_FIRST_OF_TYPE: // p:first-of-type Selects every <p> element that is the first <p> element of its parent
for (k=vParentVChildren.indexOf(vnode)-1; k>=0; k--) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':focus': // input:focus Selects the input element which has focus
if (vnode.domNode!==DOCUMENT.activeElement) {
return false;
}
break;
case ':in-range': // input:in-range Selects input elements with a value within a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || (value<min) || (value>max)) {
return false;
}
break;
case ':lang': // p:lang(it) Selects every <p> element with a lang attribute equal to "it" (Italian)
if (vnode.attrs.lang!==pseudoArg) {
return false;
}
break;
case PSEUDO_LAST_CHILD: // p:last-child Selects every <p> element that is the last child of its parent
if (vParentVChildren[vParentVChildren.length-1]!==vnode) {
return false;
}
break;
case PSEUDO_LAST_OF_TYPE: // p:last-of-type Selects every <p> element that is the last <p> element of its parent
len2 = vParentVChildren.length;
for (k=vParentVChildren.indexOf(vnode)+1; k<len2; k++) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':not': // :not(p) Selects every element that is not a <p> element
if (vnode.matchesSelector(pseudoArg)) {
return false;
}
break;
case PSEUDO_NTH_CHILD: // p:nth-child(2) Selects every <p> element that is the second child of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
index = vParentVChildren.indexOf(vnode)+1;
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_CHILD: // p:nth-last-child(2) Selects every <p> element that is the second child of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_OF_TYPE: // p:nth-last-of-type(2) Selects every <p> element that is the second <p> element of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
index = 0;
for (k=vParentVChildren.length-1; (k>=0) && !found; k--) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_OF_TYPE: // p:nth-of-type(2) Selects every <p> element that is the second <p> element of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
len2 = vParentVChildren.length;
index = 0;
for (k=0; (k<len2) && !found; k++) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_ONLY_OF_TYPE: // p:only-of-type Selects every <p> element that is the only <p> element of its parent
len2 = vParentVChildren.length;
count = 0;
for (k=0; (k<len2) && (count<=1); k++) {
(vParentVChildren[k].tag===vnode.tag) && count++;
}
if (count!==1) {
return false;
}
break;
case PSEUDO_ONLY_CHILD: // p:only-child Selects every <p> element that is the only child of its parent
if (vParentVChildren.length!==1) {
return false;
}
break;
case ':optional': // input:optional Selects input elements with no "required" attribute
if (vnode.attrs.required) {
return false;
}
break;
case ':out-of-range': // input:out-of-range Selects input elements with a value outside a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || ((value>=min) && (value<=max))) {
return false;
}
break;
case ':read-only': // input:read-only Selects input elements with the "readonly" attribute specified
if (!vnode.attrs.readonly) {
return false;
}
break;
case ':read-write': // input:read-write Selects input elements with the "readonly" attribute NOT specified
if (vnode.attrs.readonly) {
return false;
}
break;
case ':required': // input:required Selects input elements with the "required" attribute specified
if (!vnode.attrs.required) {
return false;
}
break;
case ':root': // Selects the document's root element
if (vnode.domNode!==DOCUMENT.documentElement) {
return false;
}
break;
}
}
}
return true;
};
/**
* Splits the selector into separate subselector-items that should match different elements through the tree.
* Special characters '>' and '+' are added as separate items in the hash.
*
* @method _splitSelector
* @param selector {String} the selector-item to check the match for
* @return {Array} splitted selectors
* @protected
* @private
* @since 0.0.1
*/
_splitSelector = function(selector) {
var list = [],
len = selector.length,
sel = '',
i, character, insideDataAttr;
for (i=0; i<len; i++) {
character = selector[i];
if (character==='[') {
sel += character;
insideDataAttr = true;
}
else if (character===']') {
sel += character;
insideDataAttr = false;
}
else if (insideDataAttr || !SPLIT_CHARACTER[character]) {
sel += character;
}
else {
// unique selectoritem is found, add it to the list
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
// in case the last character was '>', '+' or '~', we need to add it as a separate item
STORABLE_SPLIT_CHARACTER[character] && (list[list.length]=character);
}
}
// add the last item
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
return list;
};
_batchEmit = function() {
// we will exactly define the 'UI:'-event --> Itags will have another emitterName
MUTATION_EVENTS.each(function (mutationEvents, vnode) {
var domNode = vnode.domNode;
if (mutationEvents[EV_REMOVED]) {
domNode.emit('UI:'+EV_REMOVED);
}
else if (mutationEvents[EV_INSERTED]) {
domNode.emit('UI:'+EV_INSERTED);
}
else {
// contentchange and attributechanges can go hand in hand
mutationEvents.each(function(value, evt) {
domNode.emit('UI:'+evt, (evt===EV_CONTENT_CHANGE) ? null : {changed: value});
});
}
});
MUTATION_EVENTS.clear();
BATCH_WILL_RUN = false;
};
_emitDestroyChildren = function(vnode) {
var children = vnode.vChildren,
len = children.length,
i, vChild;
for (i=0; i<len; i++) {
vChild = children[i];
vChild._emit(EV_REMOVED);
}
};
_markRemoved = function(vnode) {
var vChildNodes = vnode.vChildNodes,
len, i, vChildNode;
if (vnode.nodeType===1) {
Object.protectedProp(vnode, 'removedFromDOM', true);
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && _markRemoved(vChildNode);
}
}
}
};
/**
* A safe way to remove a dom-node, even if it is not present.
* Will not throw a JS error when the "to be removed" node isn't in the dom
*
* @method _tryRemoveDomNode
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param childDomNode {DOMNode} the node that needs to be removed
* @since 0.0.1
*/
_tryRemoveDomNode = function(parentDomNode, childDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && childDomNode.isItag && childDomNode.isItag()) {
DOCUMENT._itagList.remove(childDomNode);
}
try {
parentDomNode._removeChild(childDomNode);
}
catch(err) {}
};
/**
* A safe way to replace a dom-node, even if the "to be replaced"-node it is not present.
* Will not throw a JS error when the "to be replaced" node isn't in the dom. in that case, the new node will
* be appended
*
* @method _tryReplaceChild
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param newChildDomNode {DOMNode} the node that needs to be replaced
* @param oldChildDomNode {DOMNode} the node that needs to be inserted
* @since 0.0.1
*/
_tryReplaceChild = function(parentDomNode, newChildDomNode, oldChildDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && oldChildDomNode.isItag && oldChildDomNode.isItag()) {
DOCUMENT._itagList.remove(oldChildDomNode);
}
try {
parentDomNode._replaceChild(newChildDomNode, oldChildDomNode);
}
catch(err) {
// if the childDomNode isn;t there any more - for whatever reason - the we need to append the newChildNode
parentDomNode._appendChild(newChildDomNode);
}
};
vNodeProto = window._ITSAmodules.VNode = {
/**
* Check whether the vnode's domNode is equal, or contains the specified Element.
*
* @method contains
* @return {Boolean} whether the vnode's domNode is equal, or contains the specified Element.
* @since 0.0.1
*/
contains: function(otherVNode, noProtectedSearch) {
var instance = this;
if (otherVNode && (otherVNode.destroyed || (noProtectedSearch && otherVNode._systemNode))) {
return false;
}
while (otherVNode && (otherVNode!==instance)) {
otherVNode = otherVNode.vParent;
if (otherVNode && noProtectedSearch && (otherVNode._systemNode || (otherVNode.isItag && otherVNode.domNode.contentHidden))) {
return false;
}
}
return (otherVNode===instance);
},
/**
* Empties the vnode.
*
* Syncs with the dom.
*
* @method empty
* @param [full=false] {Boolean} whether system-nodes should be removed as well
* @chainable
* @since 0.0.1
*/
empty: function(full) {
delete this._scripts;
return this._setChildNodes([], null, full);
},
/**
* Returns the first child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method firstOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the first child-vnode or null when not present
* @since 0.0.1
*/
firstOfVChildren: function(cssSelector) {
var instance = this,
found, i, len, vChildren, element;
if (!cssSelector) {
return instance.vFirstElementChild;
}
vChildren = instance.vChildren;
len = vChildren.length;
for (i=0; !found && (i<len); i++) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
return found;
},
/**
* Gets the innerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* Only valid for nodetype=1 (HTMLElements)
*
* @method getHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String|undefined} the innerHTML without the elements specified, or `undefined` when not an HTMLElement
* @since 0.0.1
*/
getHTML: function(exclude, includeSystemNodes) {
var instance = this,
html, vChildNodes, len, i, vChildNode;
if (instance.nodeType===1) {
if (exclude) {
Array.isArray(exclude) || (exclude=[exclude]);
}
html = '';
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
switch (vChildNode.nodeType) {
case 1:
(exclude && exclude.contains(vChildNode.domNode)) || (!includeSystemNodes && vChildNode._systemNode) || (html+=vChildNode.getOuterHTML(exclude, includeSystemNodes));
break;
case 3:
html += vChildNode.text.replace(/</g, '<').replace(/>/g, '>');
break;
case 8:
html += '<!--' + vChildNode.text.replace(/</g, '<').replace(/>/g, '>') + '-->';
}
}
}
return html;
},
/**
* Gets the outerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* @method getOuterHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String} the outerHTML
* @since 0.0.1
*/
getOuterHTML: function(exclude, includeSystemNodes) {
var instance = this,
html,
attrs = instance.attrs;
if (instance.nodeType===1) {
if (instance.nodeType!==1) {
return instance.textContent;
}
html = '<' + instance.tag.toLowerCase();
attrs.each(function(value, key) {
html += ' '+key+'="'+value+'"';
});
instance.isVoid && (html += '/');
html += '>';
if (!instance.isVoid) {
html += ((!includeSystemNodes && instance.isItag) ? '' : instance.getHTML(exclude, includeSystemNodes)) + '</' + instance.tag.toLowerCase() + '>';
}
}
return html;
},
/**
* Checks whether the vnode has any vChildNodes (nodeType of 1, 3 or 8).
*
* @method hasVChildNodes
* @return {Boolean} whether the vnode has any vChildNodes.
* @since 0.0.1
*/
hasVChildNodes: function() {
return this.vChildNodes ? (this.vChildNodes.length>0) : false;
},
/**
* Checks whether the vnode has any vChildren (vChildNodes with nodeType of 1).
*
* @method hasVChildren
* @return {Boolean} whether the vnode has any vChildren.
* @since 0.0.1
*/
hasVChildren: function() {
return this.vChildNodes ? (this.vChildren.length>0) : false;
},
/**
* Checks whether the className is present on the vnode.
*
* @method hasClass
* @param className {String|Array} the className to check for. May be an Array of classNames, which all needs to be present.
* @return {Boolean} whether the className (or classNames) is present on the vnode
* @since 0.0.1
*/
hasClass: function(className) {
var instance = this,
check = function(cl) {
return !!instance.classNames[cl];
};
if (!instance.classNames) {
return false;
}
if (typeof className === STRING) {
return check(className);
}
else if (Array.isArray(className)) {
return className.every(check);
}
return false;
},
/**
* Returns the last child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method lastOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the last child-vnode or null when not present
* @since 0.0.1
*/
lastOfVChildren: function(cssSelector) {
var vChildren = this.vChildren,
found, i, element;
if (vChildren) {
if (!cssSelector) {
return this.vLastElementChild;
}
for (i=vChildren.length-1; !found && (i>=0); i--) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
}
return found;
},
/**
* Checks whether the vnode matches one of the specified selectors. `selectors` can be one, or multiple css-selectors,
* separated by a `comma`. For example: "#myid li.red blue" is one selector, "div.red, div.blue, div.green" are three selectors.
*
* @method matchesSelector
* @param selectors {String} one or more css-selectors
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches one of the selectors
* @since 0.0.1
*/
matchesSelector: function(selectors, relatedVNode) {
var instance = this;
if (instance.nodeType!==1) {
return false;
}
selectors = selectors.split(',');
// we can use Array.some, because there won't be many separated selectoritems,
// so the final invocation won't be delayed much compared to looping
return selectors.some(function(selector) {
return _matchesOneSelector(instance, selector, relatedVNode);
});
},
/**
* Reloads the DOM-attribute into the vnode.
*
* @method matchesSelector
* @param attributeName {String} the name of the attribute to be reloaded.
* @return {Node} the domNode that was reloaded.
* @since 0.0.1
*/
reloadAttr: function(attributeName) {
var instance = this,
domNode = instance.domNode,
attributeValue = domNode._getAttribute(attributeName),
attrs = instance.attrs,
extractStyle, extractClass;
if (instance.nodeType===1) {
attributeValue || (attributeValue='');
if (attributeValue==='') {
delete attrs[attributeName];
// in case of STYLE attributeName --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attributeName --> special treatment
(attributeName===CLASS) && (instance.classNames={});
// in case of ID attributeName --> special treatment
if ((attributeName===ID) && (instance.id)) {
delete nodeids[instance.id];
delete instance.id;
}
}
else {
attrs[attributeName] = attributeValue;
// in case of STYLE attributeName --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(attributeValue);
attributeValue = extractStyle.attrStyle;
if (attributeValue) {
attrs.style = attributeValue;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attributeName --> special treatment
extractClass = extractor.extractClass(attributeValue);
attributeValue = extractClass.attrClass;
if (attributeValue) {
attrs[CLASS] = attributeValue;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (instance.id!==attributeValue) && (delete nodeids[instance.id]);
instance.id = attributeValue;
nodeids[attributeValue] = domNode;
}
}
}
return domNode;
},
/**
* Returns the vnode's style in a serialized form: the way it appears in the dom.
*
* @method serializeStyles
* @return {String} vnode's style
* @since 0.0.1
*/
serializeStyles: function() {
return extractor.serializeStyles(this.styles);
},
/**
* Sets the vnode's and dom-nodes inner HTML.
*
* Syncs with the dom. Can be invoked multiple times without issues.
*
* @method setHTML
* @param content {String} the innerHTML
* @param [suppressItagRender] {Boolean} to suppress Itags from rendering
* @param [allowScripts=false] {Boolean} whether scripts are allowed --> these should be defined with `xscript` instead of `script`
* @chainable
* @since 0.0.1
*/
setHTML: function(content, suppressItagRender, allowScripts) {
var instance = this;
instance._setChildNodes(htmlToVNodes(content, vNodeProto, instance.ns, null, suppressItagRender, allowScripts), suppressItagRender);
return instance;
},
/**
* Syncs the vnode's nodeid (if available) inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom. Can be invoked multiple times without issues.
*
* @method storeId
* @chainable
* @since 0.0.1
*/
storeId: function() {
// store node/vnode inside WeakMap:
var instance = this;
instance.id ? (nodeids[instance.id]=instance.domNode) : (delete nodeids[instance.id]);
return instance;
},
//---- private ------------------------------------------------------------------
_addToTaglist: function() {
var instance = this,
itagList;
if (instance.isItag) {
itagList = DOCUMENT.getItags(); // also reads the dom if the list isn't build yet
instance._data || Object.protectedProp(instance, '_data', {});
if (!instance._data.ce_destroyed && !itagList.contains(instance.domNode)) {
itagList.push(instance.domNode);
}
}
},
/**
* Adds a vnode to the end of the list of vChildNodes.
*
* Syncs with the DOM.
*
* @method _appendChild
* @param VNode {vnode} vnode to append
* @private
* @return {Node} the Node that was appended
* @since 0.0.1
*/
_appendChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
size, noProccessScript, scriptContent;
if ((VNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (VNode.attrs && VNode.attrs.src) ? VNode.attrs.src : VNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
VNode._moveToParent(instance);
instance.domNode._appendChild(domNode);
if (VNode.nodeType===3) {
size = instance.vChildNodes.length;
instance._normalize();
// if the size changed, then the domNode was merged
(size===instance.vChildNodes.length) || (domNode=instance.vChildNodes[instance.vChildNodes.length-1].domNode);
}
if (VNode.nodeType===1) {
VNode._addToTaglist();
VNode._emit(EV_INSERTED);
}
return domNode;
}
},
/**
* Removes the vnode from its parent vChildNodes- and vChildren-list.
*
* Does NOT sync with the dom.
*
* @method _deleteFromParent
* @private
* @chainable
* @since 0.0.1
*/
_deleteFromParent: function() {
var instance = this,
vParent = instance.vParent;
if (vParent && vParent.vChildNodes) {
vParent.vChildNodes.remove(instance);
// force to recalculate the vChildren on a next call:
(instance.nodeType===1) && (vParent._vChildren=null);
}
return instance;
},
/**
* Cleans up (empties) vnode's `_data`
*
* @method _cleanData
* @private
* @chainable
* @since 0.0.1
*/
_cleanData: function() {
var instance = this,
data = instance._data;
data && data.each(
function(value, key) {
delete data[key];
}
);
return instance;
},
/**
* Cleans up (removes) duplicated `style` definitions.
*
* @method _cleanupStyle
* @private
* @chainable
* @since 0.0.1
*/
_cleanupStyle: function() {
var instance = this,
compare = [],
removal = [],
vChildren = instance.vChildren,
len = vChildren.length,
vChild, i, styleContent, vChildInner;
for (i=0; i<len; i++) {
vChild = vChildren[i];
if (vChild.tag==='STYLE') {
vChildInner = vChild.vChildNodes[0];
if (vChildInner) {
styleContent = vChildInner.text;
if (compare.contains(styleContent)) {
removal[removal.length] = vChild;
}
else {
compare[compare.length] = styleContent;
}
}
else {
// empty style-tag
removal[removal.length] = vChild;
}
}
}
removal.forEach(function(vnode) {
instance._removeChild(vnode);
});
},
/**
* Destroys the vnode and all its vnode-vChildNodes.
* Removes it from its vParent.vChildNodes list,
* also removes its definitions inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom.
*
* @method _destroy
* @private
* @chainable
* @since 0.0.1
*/
_destroy: function(silent) {
var instance = this,
vChildNodes = instance.vChildNodes,
len, i, vChildNode, vParent, treeNodes;
if (!instance.destroyed) {
if (!silent) {
// Because we don't want to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
later(function() {
instance._emit(EV_REMOVED, null, null, null, true);
}, 5);
}
Object.protectedProp(instance, 'destroyed', true);
// first: determine the dom-tree, which module `event-dom` needs to determine where the node was before it was destroyed:
treeNodes = [instance];
vParent = instance.vParent;
while (vParent) {
treeNodes[treeNodes.length] = vParent;
vParent = vParent.vParent;
}
// mark all its vChildNodes so we can see if the node is in the DOM
_markRemoved(instance);
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && instance.isItag) {
DOCUMENT._itagList.remove(instance.domNode);
}
instance._scripts && (instance._scripts.length=0);
// The definite cleanup needs to be done after a timeout:
// someone might need to handle the Element when removed (fe to cleanup specific things)
later(function() {
instance._cleanData();
if (instance.nodeType===1) {
// _destroy all its vChildNodes
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && vChildNode._destroy(true);
}
}
}
instance._vChildren = null;
// explicitely set instance.domNode._vnode and instance.domNode to null in order to prevent problems with the GC (we break the circular reference)
delete instance.domNode._vnode;
// if valid id, then _remove the DOMnodeRef from internal hash
instance.id && delete nodeids[instance.id];
}, silent ? 0 : DESTROY_DELAY);
instance._deleteFromParent();
// Do not make domNode `null` --> it could be used even when not in the dom
}
return instance;
},
_emit: function(evt, attribute, newValue, prevValue, destroyEvt) {
/**
* Emitted by every Element that gets inserted.
*
* @event nodeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets removed.
*
* @event noderemove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets its content changed (innerHTML/innerText).
*
* @event nodecontentchange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute inserted.
*
* @event attributeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* </ul>
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute removed.
*
* @event attributeremove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Strings of the attributeNames that are removed
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute changed.
*
* @event attributechange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* <li>prevValue</li>
* </ul>
* @since 0.1
*/
var instance = this,
silent, attrMutations, mutationEvents, mutation, vParent;
if (!DOCUMENT.hasMutationSubs || (instance.nodeType!==1)) {
return;
}
silent = !!DOCUMENT._suppressMutationEvents;
if (!silent && (destroyEvt || !instance.destroyed)) {
// Because we don't wannt to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
mutationEvents = MUTATION_EVENTS.get(instance) || {};
if (attribute) {
attrMutations = mutationEvents[evt] || [];
if (evt===EV_ATTRIBUTE_REMOVED) {
mutation = attribute;
}
else {
mutation = {
attribute: attribute
};
if ((evt===EV_ATTRIBUTE_INSERTED) || (evt===EV_ATTRIBUTE_CHANGED)) {
mutation.newValue = newValue;
}
if ((evt===EV_ATTRIBUTE_CHANGED) && prevValue) {
mutation.prevValue = prevValue;
}
}
attrMutations.push(mutation);
mutationEvents[evt] = attrMutations;
}
else {
mutationEvents[evt] = true;
}
MUTATION_EVENTS.set(instance, mutationEvents);
// now set all parent to have a nodecontentchange:
vParent = instance.vParent;
vParent && vParent._emit(EV_CONTENT_CHANGE);
// in case of removal we need to emit EV_REMOVED for all children right now
// for they will be actually removed silently after a delay of 1 minute
(evt===EV_REMOVED) && _emitDestroyChildren(instance);
if (!BATCH_WILL_RUN) {
BATCH_WILL_RUN = true;
async(function() {
_batchEmit();
});
}
}
return instance;
},
/**
* Inserts `newVNode` before `refVNode`.
*
* Syncs with the DOM.
*
* @method _insertBefore
* @param newVNode {vnode} vnode to insert
* @param refVNode {vnode} The vnode before which newVNode should be inserted.
* @private
* @return {Node} the Node being inserted (equals domNode)
* @since 0.0.1
*/
_insertBefore: function(newVNode, refVNode) {
var instance = this,
domNode = newVNode.domNode,
index = instance.vChildNodes.indexOf(refVNode),
noProccessScript, scriptContent;
if ((newVNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (newVNode.attrs && newVNode.attrs.src) ? newVNode.attrs.src : newVNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
if (index!==-1) {
newVNode._moveToParent(instance, index);
instance.domNode._insertBefore(domNode, refVNode.domNode);
(newVNode.nodeType===3) && instance._normalize();
if (newVNode.nodeType===1) {
newVNode._addToTaglist();
newVNode._emit(EV_INSERTED);
}
return domNode;
}
else {
console.warn('trying to insert before, but no ref-node found. Will append the new node');
return instance._appendChild(newVNode);
}
}
},
/**
* Moves the vnode from its current parent.vChildNodes list towards a new parent vnode at the specified position.
*
* Does NOT sync with the dom.
*
* @method _moveToParent
* @param parentVNode {vnode} the parent-vnode
* @param [index] {Number} the position of the child. When not specified, it will be appended.
* @private
* @chainable
* @since 0.0.1
*/
_moveToParent: function(parentVNode, index) {
var instance = this,
vParent = instance.vParent;
instance._deleteFromParent();
instance.vParent = parentVNode;
parentVNode.vChildNodes || (parentVNode.vChildNodes=[]);
(typeof index==='number') ? parentVNode.vChildNodes.insertAt(instance, index) : (parentVNode.vChildNodes[parentVNode.vChildNodes.length]=instance);
// force to recalculate the vChildren on a next call:
vParent && (instance.nodeType===1) && (vParent._vChildren = null);
// force to recalculate the vChildren on a next call:
parentVNode && (instance.nodeType===1) && (parentVNode._vChildren=null);
return instance;
},
/**
* Removes empty TextNodes and merges following TextNodes inside the vnode.
*
* Syncs with the dom.
*
* @method _normalize
* @private
* @chainable
* @since 0.0.1
*/
_normalize: function() {
var instance = this,
domNode = instance.domNode,
vChildNodes = instance.vChildNodes,
changed = false,
i, preChildNode, vChildNode;
if (!instance._unNormalizable && vChildNodes) {
for (i=vChildNodes.length-1; i>=0; i--) {
vChildNode = vChildNodes[i];
preChildNode = vChildNodes[i-1]; // i will get the value `-1` eventually, which leads into undefined preChildNode
if (vChildNode.nodeType===3) {
if (vChildNode.text==='') {
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
else if (preChildNode && preChildNode.nodeType===3) {
preChildNode.text += vChildNode.text;
preChildNode.domNode.nodeValue = unescapeEntities(preChildNode.text);
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
}
}
}
changed && instance._emit(EV_CONTENT_CHANGE);
return instance;
},
/**
* Makes the vnode `normalizable`. Could be set to `false` when batch-inserting nodes, while `normalizaing` manually at the end.
* Afterwards, you should always reset `normalizable` to true.
*
* @method _normalizable
* @param value {Boolean} whether the vnode should be normalisable.
* @private
* @chainable
* @since 0.0.1
*/
_normalizable: function(value) {
var instance = this;
value ? (delete instance._unNormalizable) : (instance._unNormalizable=true);
return instance;
},
/**
* Prevents MutationObserver from making the dom sync with the vnode.
* Should be used when manipulating the dom from within the vnode itself (to preventing looping)
*
* @method _noSync
* @chainable
* @private
* @since 0.0.1
*/
_noSync: function() {
var instance = this;
if (!instance._nosync) {
instance._nosync = true;
async(function() {
instance._nosync = false;
});
}
return instance;
},
/**
* Removes the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _removeAttr
* @param attributeName {String}
* @private
* @chainable
* @since 0.0.1
*/
_removeAttr: function(attributeName, suppressItagRender) {
var instance = this,
attributeNameSplitted, ns;
if (instance.isItag && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
console.warn('Not allowed to remove the attribute '+attributeName);
return instance;
}
if (instance.attrs[attributeName]!==undefined) {
delete instance.attrs[attributeName];
// in case of STYLE attribute --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attribute --> special treatment
(attributeName===CLASS) && (instance.classNames={});
if (attributeName===ID) {
delete nodeids[instance.id];
delete instance.id;
}
instance._emit(EV_ATTRIBUTE_REMOVED, attributeName);
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
instance.domNode._removeAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName);
}
else {
instance.domNode._removeAttribute(attributeName);
}
}
return instance;
},
/**
* Removes the vnode's child-vnode from its vChildren and the DOM.
*
* Syncs with the DOM.
*
* @method removeChild
* @param VNode {vnode} the child-vnode to remove
* @private
* @since 0.0.1
*/
_removeChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
hadFocus = domNode && domNode.hasFocus && domNode.hasFocus() && (VNode.attrs['fm-lastitem']==='true'),
parentVNode = VNode.vParent;
VNode._destroy();
_tryRemoveDomNode(instance.domNode, VNode.domNode);
instance._normalize();
// now, reset the focus on focusmanager when needed:
if (hadFocus) {
while (parentVNode && !parentVNode.attrs['fm-manage']) {
parentVNode = parentVNode.vParent;
}
parentVNode && parentVNode.domNode.focus();
}
},
/**
* Replaces the current vnode at the parent.vChildNode list by `newVNode`
*
* Does NOT sync with the dom.
*
* @method _replaceAtParent
* @param newVNode {Object} the new vnode which should take over the place of the current vnode
* @private
* @chainable
* @since 0.0.1
*/
_replaceAtParent: function(newVNode) {
var instance = this,
vParent = instance.vParent,
vChildNodes, index;
if (vParent && (vChildNodes=vParent.vChildNodes)) {
index = vChildNodes.indexOf(instance);
// force to recalculate the vChildren on a next call:
((instance.nodeType===1) || (newVNode.nodeType===1)) && (instance.vParent._vChildren=null);
vChildNodes[index] = newVNode;
}
return instance._destroy();
},
/**
* Sets the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _setAttr
* @param attributeName {String}
* @param value {String} the value for the attributeName
* @param [force=false] {Boolean} force the attribute to be set, even if restrictions would deny it
* @private
* @chainable
* @since 0.0.1
*/
_setAttr: function(attributeName, value, force, suppressItagRender) {
var instance = this,
extractStyle, extractClass,
attrs = instance.attrs,
prevVal = attrs[attributeName],
domNode = instance.domNode,
attributeNameSplitted, ns;
if (instance.isItag && !force && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
if (prevVal!=value) {
console.warn('Not allowed to set the attribute '+attributeName);
}
return instance;
}
// don't check by !== --> value isn't parsed into a String yet
if (prevVal && ((value===undefined) || (value===null))) {
instance._removeAttr(attributeName, suppressItagRender);
return instance;
}
// attribute-values are always Strings:
value = String(value);
// attribute-values will be stored without " or '
value = value.replace(/"/g, '"').replace(/'/g, "'");
if (prevVal!=value) {
attrs[attributeName] = value;
// in case of STYLE attribute --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(value);
value = extractStyle.attrStyle;
if (value) {
attrs.style = value;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attribute --> special treatment
extractClass = extractor.extractClass(value);
value = extractClass.attrClass;
if (value) {
attrs[CLASS] = value;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (delete nodeids[instance.id]);
instance.id = value;
nodeids[value] = domNode;
}
instance._emit(prevVal ? EV_ATTRIBUTE_CHANGED : EV_ATTRIBUTE_INSERTED, attributeName, value, prevVal);
// when set in the dom --> quotes need to be set as "
value = value.replace(/"/g, '"');
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
domNode._setAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName, value);
}
else {
domNode._setAttribute(attributeName, value);
}
}
return instance;
},
/**
* Redefines the attributes of both the vnode as well as its related dom-node. The new
* definition replaces any previous attributes (without touching unmodified attributes).
* the `is` attribute cannot be changed for itags.
*
* Syncs the new vnode's attributes with the dom.
*
* @method _setAttrs
* @param newAttrs {Object|Array} the new attributes to be set
* @private
* @chainable
* @since 0.0.1
*/
_setAttrs: function(newAttrs, suppressItagRender) {
// does sync the DOM
var instance = this,
attrsObj, attr, attrs, i, key, keys, len, value;
if (instance.nodeType!==1) {
return;
}
instance._noSync();
attrs = instance.attrs;
attrs.id && (delete nodeids[attrs.id]);
if (Object.isObject(newAttrs)) {
attrsObj = newAttrs;
}
else {
attrsObj = {};
len = newAttrs.length;
for (i=0; i<len; i++) {
attr = newAttrs[i];
attrsObj[attr.name] = attr.value;
}
}
if (instance.isItag && !suppressItagRender) {
if (attrs.is) {
attrsObj.is = attrs.is;
}
else {
delete attrsObj.is;
delete attrsObj.Is;
delete attrsObj.iS;
delete attrsObj.IS;
}
}
// first _remove the attributes that are no longer needed.
// quickest way for object iteration: http://jsperf.com/object-keys-iteration/20
keys = Object.keys(attrs);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
attrsObj[key] || instance._removeAttr(key, suppressItagRender);
}
// next: every attribute that differs: redefine
keys = Object.keys(attrsObj);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
value = attrsObj[key];
(attrs[key]===value) || instance._setAttr(key, value, suppressItagRender);
}
return instance;
},
/**
* Redefines the childNodes of both the vnode as well as its related dom-node. The new
* definition replaces any previous nodes. (without touching unmodified nodes).
*
* Syncs the new vnode's childNodes with the dom.
*
* @method _setChildNodes
* @param newVChildNodes {Array} array with vnodes which represent the new childNodes
* @private
* @chainable
* @since 0.0.1
*/
_setChildNodes: function(newVChildNodes, suppressItagRender, removeSystemElements) {
// does sync the DOM
var instance = this,
vChildNodes = instance.vChildNodes || [],
domNode = instance.domNode,
forRemoval = [],
i, oldChild, newChild, newLength, len, len2, childDomNode, nodeswitch, bkpAttrs, cleanupStyle,
process, bkpChildNodes, needNormalize, prevSuppress, scriptContent, _scripts, scriptLen, j;
instance._noSync();
// first: reset ._vChildren --> by making it empty, its getter will refresh its list on a next call
instance._vChildren = null;
// if newVChildNodes is undefined, then we assume it to be empty --> an empty array
newVChildNodes || (newVChildNodes=[]);
// quickest way to loop through array is by using for loops: http://jsperf.com/array-foreach-vs-for-loop/5
len = vChildNodes.length;
// we need to add the systemNodes -if any- to the new newVChildNodes: they should be retained.
// SystemNodes are always places at the beginning, so they won't get reshuffled:
if (!removeSystemElements) {
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
if (oldChild._systemNode) {
newVChildNodes.insertAt(oldChild, i);
}
else {
break;
}
}
}
newLength = newVChildNodes.length;
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
childDomNode = oldChild.domNode;
if (i < newLength) {
newChild = newVChildNodes[i];
newChild.vParent || (newChild.vParent=instance);
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
/*jshint boss:true */
switch (nodeswitch=NODESWITCH[oldChild.nodeType][newChild.nodeType]) {
/*jshint boss:false */
case 1: // oldNodeType==Element, newNodeType==Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
if ((oldChild.tag!==newChild.tag) ||
((oldChild.tag===newChild.tag) && oldChild.isItag && (oldChild.attrs.is!==newChild.attrs.is))) {
// new tag --> completely replace
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
/*jshint proto:true */
oldChild.isItag && oldChild.domNode.destroyUI && oldChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
newChild.attrs = {}; // reset to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
oldChild._replaceAtParent(newChild);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
else {
// same tag --> only update what is needed
// NOTE: when this._unchangableAttrs exists, an itag-element syncs its UI -->
if (oldChild._data) {
// we might need to set the class `itag-rendered` when the attributeData says so:
// this happens when an itag gets refreshed with an unrendered definition
if (oldChild._data.itagRendered && !newChild.hasClass('itag-rendered')) {
newChild.classNames['itag-rendered'] = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' '+'itag-rendered';
}
else {
newChild.attrs[CLASS] = 'itag-rendered';
}
}
// we might need to set the class `focussed` when the attributeData says so:
// this happens when an itag gets rerendered: its renderFn doesn't know if any elements
// were focussed
if (oldChild._data.focussed && !newChild.hasClass('focussed')) {
newChild.classNames.focussed = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' ' + 'focussed';
}
else {
newChild.attrs[CLASS] = 'focussed';
}
}
if (oldChild._data['fm-tabindex']) {
// node has the tabindex set by the focusmanager,
// but that info might got lost with re-rendering of the new element
newChild.attrs.tabindex = '0';
}
}
if (oldChild.isItag) {
prevSuppress = DOCUMENT._suppressMutationEvents || false;
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
/*jshint proto:true */
newChild.domNode.destroyUI && newChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
oldChild._setAttrs(newChild.attrs, suppressItagRender);
newChild._destroy(true); // destroy through the vnode and removing from DOCUMENT._itagList
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(prevSuppress);
}
else {
oldChild._setAttrs(newChild.attrs, suppressItagRender);
// next: sync the vChildNodes:
oldChild._setChildNodes(newChild.vChildNodes, suppressItagRender);
}
// reset ref. to the domNode, for it might have been changed by newChild:
oldChild.id && (nodeids[oldChild.id]=childDomNode);
newVChildNodes[i] = oldChild;
}
}
break;
case 2: // oldNodeType==Element, newNodeType==TextNode
// case2 and case3 should be treated the same
case 3: // oldNodeType==Element, newNodeType==Comment
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
oldChild._replaceAtParent(newChild);
instance._emit(EV_CONTENT_CHANGE);
break;
case 4: // oldNodeType==TextNode, newNodeType==Element
// case4 and case7 should be treated the same
case 7: // oldNodeType==Comment, newNodeType==Element
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
// oldChild.isVoid = newChild.isVoid;
// delete oldChild.text;
instance._emit(EV_CONTENT_CHANGE);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
break;
case 5: // oldNodeType==TextNode, newNodeType==TextNode
// case5 and case9 should be treated the same
case 9: // oldNodeType==Comment, newNodeType==Comment
if (oldChild.text!==newChild.text) {
oldChild.text = newChild.text;
oldChild.domNode.nodeValue = unescapeEntities(newChild.text);
instance._emit(EV_CONTENT_CHANGE);
}
newVChildNodes[i] = oldChild;
break;
case 6: // oldNodeType==TextNode, newNodeType==Comment
// case6 and case8 should be treated the same
case 8: // oldNodeType==Comment, newNodeType==TextNode
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = oldChild.vParent;
instance._emit(EV_CONTENT_CHANGE);
}
if ((nodeswitch===2) || (nodeswitch===5) || (nodeswitch===8)) {
needNormalize = true;
}
}
else {
// _remove previous definition
_tryRemoveDomNode(domNode, oldChild.domNode);
// the oldChild needs to be removed, however, this cannot be done right now, for it would effect the loop
// so we store it inside a hash to remove it later
forRemoval[forRemoval.length] = oldChild;
}
}
// now definitely remove marked childNodes:
len2 = forRemoval.length;
for (i=0; i<len2; i++) {
forRemoval[i]._destroy();
}
// now we add all new vChildNodes that go beyond `len`:
for (i = len; i < newLength; i++) {
newChild = newVChildNodes[i];
newChild.vParent = instance;
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
switch (newChild.nodeType) {
case 1: // Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
domNode._appendChild(newChild.domNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
break;
case 3: // TextNode
needNormalize = true;
// we need to break through --> no `break`
/* falls through */
default: // TextNode or CommentNode
// newChild.domNode.nodeValue = newChild.text;
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
domNode._appendChild(newChild.domNode);
instance._emit(EV_CONTENT_CHANGE);
}
newChild.storeId();
}
instance.vChildNodes = newVChildNodes;
needNormalize && instance._normalize();
cleanupStyle && instance._cleanupStyle();
return instance;
},
_setUnchangableAttrs: function(unchangableObj) {
this._unchangableAttrs = unchangableObj;
}
};
//---- properties ------------------------------------------------------------------
/**
* A hash of all the `attributes` of the vnode's representing dom-node.
*
* @property attrs
* @type Object
* @since 0.0.1
*/
/**
* Hash with all the classes of the vnode. Every class represents a key, all values are set `true`.
*
* @property classNames
* @type Object
* @since 0.0.1
*/
/**
* The `id` of the vnode's representing dom-node (if any).
*
* @property id
* @type String
* @since 0.0.1
*/
/**
* Tells whether tag is a void Element. Examples are: `br`, `img` and `input`. Non-void Elements are f.e. `div` and `table`.
* For TextNodes and CommentNodes, this property is `undefined`.
*
* @property isVoid
* @type Boolean
* @since 0.0.1
*/
/**
* The `nodeType` of the vnode's representing dom-node (1===ElementNode, 3===TextNode, 8===CommentNode).
*
* @property nodeType
* @type Number
* @since 0.0.1
*/
/**
* The `tag` of the vnode's representing dom-node (allways uppercase).
*
* @property tag
* @type String
* @since 0.0.1
*/
/**
* The `content` of the vnode's representing dom-node, in case it is a TextNode or CommentNode.
* Equals dom-node.nodeValue.
*
* Is `undefined` for ElementNodes.
*
* @property text
* @type String
* @since 0.0.1
*/
/**
* Hash with all the childNodes (vnodes). vChildNodes are any kind of vnodes (nodeType===1, 3 or 8)
*
* @property vChildNodes
* @type Array
* @since 0.0.1
*/
/**
* The underlying `dom-node` that the vnode represents.
*
* @property domNode
* @type domNode
* @since 0.0.1
*/
/**
* vnode's parentNode (defined as a vnode itself).
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
Object.defineProperties(vNodeProto, {
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property innerHTML
* @type String
* @since 0.0.1
*/
innerHTML: {
get: function() {
return this.getHTML();
},
set: function(v) {
this.setHTML(v);
}
},
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property nodeValue
* @type String
* @since 0.0.1
*/
nodeValue: {
get: function() {
var instance = this;
return ((instance.nodeType===3) || (instance.nodeType===8)) ? instance.text : null;
},
set: function(v) {
var instance = this,
newTextContent, prevTextContent;
if ((instance.nodeType===3) || (instance.nodeType===8)) {
prevTextContent = instance.domNode.textContent;
instance.domNode.textContent = v;
// set .text AFTER the dom-node is updated --> the content might be escaped!
newTextContent = instance.text = instance.domNode.textContent;
(newTextContent!==prevTextContent) && instance._emit(EV_CONTENT_CHANGE);
}
}
},
/**
* Gets or sets the outerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property outerHTML
* @type String
* @since 0.0.1
*/
outerHTML: {
get: function() {
return this.getOuterHTML();
},
set: function(v) {
var instance = this,
vParent = instance.vParent,
id = instance.attrs.id,
vnode, vnodes, bkpAttrs, bkpChildNodes, i, len, vChildNodes, isLastChildNode, index, refDomNode;
if ((instance.nodeType!==1) || !vParent) {
return;
}
instance._noSync();
vChildNodes = vParent.vChildNodes;
index = vChildNodes.indexOf(instance);
isLastChildNode = (index===(vChildNodes.length-1));
isLastChildNode || (refDomNode=vChildNodes[index+1].domNode);
vnodes = htmlToVNodes(v, vNodeProto, vParent.ns, vParent);
len = vnodes.length;
if (len>0) {
// the first vnode will replace the current instance:
vnode = vnodes[0];
if (vnode.nodeType===1) {
if (vnode.tag!==instance.tag) {
// new tag --> completely replace
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
id && (delete nodeids[id]);
vnode.attrs = {}; // reset to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
// vnode.attrs = bkpAttrs;
// vnode.vChildNodes = bkpChildNodes;
vnode.id && (nodeids[vnode.id]=vnode.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
else {
instance._setAttrs(vnode.attrs);
instance._setChildNodes(vnode.vChildNodes);
}
}
else {
id && (delete nodeids[id]);
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
}
for (i=1; i<len; i++) {
vnode = vnodes[i];
switch (vnode.nodeType) {
case 1: // Element
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
vnode.attrs = {}; // reset, to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._insertBefore(vnode.domNode, refDomNode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
break;
default: // TextNode or CommentNode
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._appendChild(vnode.domNode, refDomNode);
}
vnode.storeId();
vnode._moveToParent(vParent, index+i);
}
}
},
/**
* Gets or sets the innerContent of the Node as plain text.
*
* The setter syncs with the DOM.
*
* @property textContent
* @type String
* @since 0.0.1
*/
textContent: {
get: function() {
var instance = this,
text = '',
vChildNodes = instance.vChildNodes,
len, i, vChildNode;
if (instance.nodeType===1) {
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
text += (vChildNode.nodeType===3) ? vChildNode.text : ((vChildNode.nodeType===1) ? vChildNode.textContent : '');
}
}
else {
text = instance.text;
}
return text;
},
set: function(v) {
var vnode = Object.create(vNodeProto);
vnode.domNode = DOCUMENT.createTextNode(v);
// create circular reference:
vnode.domNode._vnode = vnode;
vnode.nodeType = 3;
vnode.text = vnode.domNode.textContent;
this._setChildNodes([vnode]);
}
},
/**
* Hash with all the children (vnodes). vChildren are vnodes that have a representing dom-node that is an HtmlElement (nodeType===1)
*
* @property vChildren
* @type Array
* @since 0.0.1
*/
vChildren: {
get: function() {
var instance = this,
children = instance._vChildren,
vChildNode, vChildNodes, i, len;
vChildNodes = instance.vChildNodes;
if (vChildNodes && !children) {
children = instance._vChildren = [];
len = vChildNodes.length;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
(vChildNode.nodeType===1) && (children[children.length]=vChildNode);
}
}
children || (children = instance._vChildren = []);
return children;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirst
* @type vnode
* @since 0.0.1
*/
vFirst: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstChild;
}
},
/**
* Reference to the first vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirstChild
* @type vnode
* @since 0.0.1
*/
vFirstChild: {
get: function() {
return (this.vChildNodes && this.vChildNodes[0]) || null;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vFirstElement
* @type vnode
* @since 0.0.1
*/
vFirstElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstElementChild;
}
},
/**
* Reference to the first vChild, where the related dom-node an Element (nodeType===1).
*
* @property vFirstElementChild
* @type vnode
* @since 0.0.1
*/
vFirstElementChild: {
get: function() {
return this.vChildren[0] || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLast
* @type vnode
* @since 0.0.1
*/
vLast: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastChild;
}
},
/**
* Reference to the last vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLastChild
* @type vnode
* @since 0.0.1
*/
vLastChild: {
get: function() {
var vChildNodes = this.vChildNodes;
return (vChildNodes && vChildNodes[vChildNodes.length-1]) || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vLastElement
* @type vnode
* @since 0.0.1
*/
vLastElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastElementChild;
}
},
/**
* Reference to the last vChild, where the related dom-node an Element (nodeType===1).
*
* @property vLastElementChild
* @type vnode
* @since 0.0.1
*/
vLastElementChild: {
get: function() {
var vChildren = this.vChildren;
return vChildren[vChildren.length-1] || null;
}
},
/**
* the Parent vnode
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
/**
* Reference to the next of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vNext
* @type vnode
* @since 0.0.1
*/
vNext: {
get: function() {
return _findNodeSibling(this, true);
}
},
/**
* Reference to the next of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vNextElement
* @type vnode
* @since 0.0.1
*/
vNextElement: {
get: function() {
return _findElementSibling(this, true);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vPrevious
* @type vnode
* @since 0.0.1
*/
vPrevious: {
get: function() {
return _findNodeSibling(this);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vPreviousElement
* @type vnode
* @since 0.0.1
*/
vPreviousElement: {
get: function() {
return _findElementSibling(this);
}
}
});
return vNodeProto;
};
},{"./attribute-extractor.js":3157,"./html-parser.js":3161,"./vdom-ns.js":3163,"js-ext/extra/hashmap.js":3115,"js-ext/extra/lightmap.js":3116,"js-ext/lib/array.js":3117,"js-ext/lib/object.js":3118,"js-ext/lib/string.js":3120,"polyfill":3137,"utils/lib/timers.js":3140}],3165:[function(require,module,exports){
arguments[4][100][0].apply(exports,arguments)
},{"./partials/extend-document.js":3159,"./partials/extend-element.js":3160,"./partials/node-parser.js":3162,"js-ext/extra/hashmap.js":3115,"js-ext/lib/object.js":3118,"utils/lib/timers.js":3140}],3166:[function(require,module,exports){
module.exports=require(14)
},{}],3167:[function(require,module,exports){
module.exports=require(15)
},{}],3168:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3166,"./lib/window.console.js":3167}],3169:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3170,"./lib/timers.js":3171}],3170:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":2985,"polyfill/polyfill-base.js":3174}],3171:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3174}],3172:[function(require,module,exports){
module.exports=require(14)
},{}],3173:[function(require,module,exports){
module.exports=require(15)
},{}],3174:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3172,"./lib/window.console.js":3173}],3175:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":3176}],3176:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":3177,"js-ext/lib/object.js":3178}],3177:[function(require,module,exports){
module.exports=require(4)
},{}],3178:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3177,"polyfill/polyfill-base.js":3181,"utils":3182}],3179:[function(require,module,exports){
module.exports=require(14)
},{}],3180:[function(require,module,exports){
module.exports=require(15)
},{}],3181:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3179,"./lib/window.console.js":3180}],3182:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3183,"./lib/timers.js":3184}],3183:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3177,"polyfill/polyfill-base.js":3187}],3184:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3187}],3185:[function(require,module,exports){
module.exports=require(14)
},{}],3186:[function(require,module,exports){
module.exports=require(15)
},{}],3187:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3185,"./lib/window.console.js":3186}],3188:[function(require,module,exports){
"use strict";
/**
* Extends io by adding the method `readXML` to it.
* Should be called using the provided `mergeInto`-method like this:
*
* @example
* var IO = require("io");
* var IOcors = require("io-cors");
* IOcors.mergeInto(IO);
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module io
* @submodule io-cors
* @class IO
* @since 0.0.1
*/
require('js-ext/lib/object.js');
var NAME = '[io-cors-ie9]: ',
createHashMap = require('js-ext/extra/hashmap.js').createMap,
XmlDOMParser = require('xmldom').DOMParser,
UNKNOW_ERROR = 'Unknown XDR-error', // XDR doesn't specify the error
REGEXP_EXTRACT_URL = new RegExp("^((([a-z][a-z0-9-.]*):\/\/)?(([^\/?#:]+)(:(\\d+))?)?)?(\/?[a-z0-9-._~%!$&'()*+,;=@]+(\/[a-z0-9-._~%!$&'()*+,;=:@]+)*\/?|\/)?([#?](.*)|$)", "i"),
currentDomain,
BODY_METHODS = createHashMap({
POST: 1,
PUT: 1
}),
VALID_XDR_METHODS = createHashMap({
GET: 1,
POST: 1
});
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.IO_Cors) {
return window._ITSAmodules.IO_Cors; // IO_Cors was already created
}
var IO = require('../io.js')(window),
isCrossDomain = function (url) {
var domain;
if (window.navigator.userAgent==='fake') {
return false;
}
domain = url.match(REGEXP_EXTRACT_URL)[1]; // will be undefined for relative url's
// in case of absoulte url: make it lowercase:
domain && (domain.toLowerCase());
// get the browserdomain:
currentDomain || (currentDomain=window.location.href.match(REGEXP_EXTRACT_URL)[1].toLowerCase());
// crossdomain will only be true with absolute url's which differ from browser domain:
return domain && (currentDomain !== domain);
},
entendXHR = function(xhr, props, options /*, promise */) {
var crossDomain;
if (!props._isXHR2) {
crossDomain = isCrossDomain(options.url);
if (crossDomain && !props._isXDR) {
if (typeof window.XDomainRequest !== 'undefined') {
xhr = new window.XDomainRequest();
props._isXDR = true;
}
}
props._CORS_IE = crossDomain && props._isXDR;
}
props._CORS_IE && !VALID_XDR_METHODS[options.method] && (options.method=(BODY_METHODS[options.method] ? 'POST' : 'GET'));
// TODO: check how to deal with opera-mini
return xhr;
},
readyHandleXDR = function(xhr, promise, headers /*, method */) {
if (xhr._isXDR) {
console.log(NAME, 'readyHandleXDR');
// for XDomainRequest, we need 'onload' instead of 'onreadystatechange'
xhr.onload || (xhr.onload=function() {
var responseText = xhr.responseText,
xmlRequest = headers && (headers.Accept==='text/xml'),
responseobject;
clearTimeout(xhr._timer);
console.log(NAME, 'xhr.onload invokes with responseText='+responseText);
// to remain consisten with XHR, we define an object with the same structure
responseobject = {
_contenttype: xhr.contentType,
responseText: responseText,
responseXML: xmlRequest ? new XmlDOMParser().parseFromString(responseText) : null,
readyState: 4,
status: 200, // XDomainRequest returns only OK or Error
// XDomainRequest only returns the header Content-Type:
getAllResponseHeaders: function () {
return 'Content-Type: '+this._contenttype;
},
getResponseHeader: function (name) {
if (name==='Content-Type') {
return this._contenttype;
}
}
};
promise.fulfill(responseobject);
});
xhr.onerror || (xhr.onerror=function() {
clearTimeout(xhr._timer);
promise.reject(UNKNOW_ERROR);
});
}
};
IO._xhrList.push(entendXHR);
IO._xhrInitList.push(readyHandleXDR);
window._ITSAmodules.IO_Cors = IO;
return IO;
};
},{"../io.js":3192,"js-ext/extra/hashmap.js":3212,"js-ext/lib/object.js":3217,"xmldom":5930}],3189:[function(require,module,exports){
"use strict";
require('js-ext/lib/object.js');
var NAME = '[io-stream]: ',
createHashMap = require('js-ext/extra/hashmap.js').createMap,
UNKNOW_ERROR = 'Unknown XDR-error'; // XDR doesn't specify the error
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.IO_Stream) {
return window._ITSAmodules.IO_Stream; // IO_Stream was already created
}
var IO = require('../io.js')(window),
/*
* Adds properties to the xhr-object: in case of streaming,
* xhr._isStream is set and xhr._isXDR might be set in case of IE<10
*
* @method _progressHandle
* @param xhr {Object} containing the xhr-instance
* @param props {Object} the propertie-object that is added too xhr and can be expanded
* @param options {Object} options of the request
* @private
*/
_entendXHR = function(xhr, props, options /*, promise */) {
if (typeof options.streamback === 'function') {
if (!props._isXHR2 && !props._isXDR) {
if (typeof window.XDomainRequest !== 'undefined') {
xhr = new window.XDomainRequest();
props._isXDR = true;
}
}
props._isStream = props._isXHR2 || props._isXDR;
}
return xhr;
},
/*
* Adds extra initialisation of the xhr-object: in case of streaming,
* an `onprogress`-handler is set up
*
* @method _progressHandle
* @param xhr {Object} containing the xhr-instance
* @param promise {Promise} reference to the Promise created by xhr
* @private
*/
_progressHandle = function(xhr, promise /*, headers, method */) {
if (xhr._isStream) {
console.log(NAME, 'progressHandle');
xhr._progressPos = 0;
xhr.onprogress = function() {
console.log(NAME, 'xhr.onprogress received data #'+xhr.responseText+'#');
var data = xhr.responseText.substr(xhr._progressPos);
// backup the fact that streaming occured
xhr._gotstreamed = true;
xhr._parseStream && (data=xhr._parseStream(data));
promise.callback(data);
xhr._progressPos = xhr.responseText.length;
};
}
},
/**
* Adds 2 methods on the xhr-instance which are used by xhr when events occur:
*
* xhr.onload()
* xhr.onerror() // only XMLHttpRequest2
*
* These events are only added in case of XDR
*
* @method _readyHandleXDR
* @param xhr {Object} containing the xhr-instance
* @param promise {Promise} reference to the Promise created by xhr
* @private
*/
_readyHandleXDR = function(xhr, promise /*, headers, method */) {
if (xhr._isXDR) {
console.log(NAME, 'readyHandleXDR');
// for XDomainRequest, we need 'onload' instead of 'onreadystatechange'
xhr.onload = function() {
clearTimeout(xhr._timer);
console.log(NAME, 'xhr.onload invokes with responseText='+xhr.responseText);
if (xhr._isStream && !xhr._gotstreamed) {
xhr.onprogress(xhr.responseText);
}
promise.fulfill(xhr);
};
xhr.onerror = function() {
clearTimeout(xhr._timer);
promise.reject(UNKNOW_ERROR);
};
}
},
/**
* Adds a `headers` X-Stream=true in case of a streaming request.
*
* @method _setHeaders
* @param xhr {Object} containing the xhr-instance
* @param headers {Object} containing all headers
* @param method {String} the request-method used
* @private
*/
_setStreamHeader = function(xhr /*, promise, headers, method */) {
if (xhr._isStream && !xhr._isXDR) {
console.log(NAME, '_setStreamHeader');
xhr.setRequestHeader('X-Stream', 'true');
}
};
IO._xhrList.push(_entendXHR);
IO._xhrInitList.push(_readyHandleXDR);
IO._xhrInitList.push(_progressHandle);
IO._xhrInitList.push(_setStreamHeader);
window._ITSAmodules.IO_Stream = IO;
return IO;
};
},{"../io.js":3192,"js-ext/extra/hashmap.js":3212,"js-ext/lib/object.js":3217}],3190:[function(require,module,exports){
"use strict";
/**
* Extends io by adding the method `readXML` to it.
* Should be called using the provided `mergeInto`-method like this:
*
* @example
* var IO = require("io");
* var IOtransfer = require("io-transfer");
* IOtransfer.mergeInto(IO);
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module io
* @submodule io-transfer
* @class IO
* @since 0.0.1
*/
require('js-ext/lib/string.js');
require('js-ext/lib/object.js');
require('polyfill/polyfill-base.js');
/*jshint proto:true */
var NAME = '[io-transfer]: ',
createHashMap = require('js-ext/extra/hashmap.js').createMap,
PROTO_SUPPORTED = !!Object.__proto__,
REVIVER = function(key, value) {
return ((typeof value==='string') && value.toDate()) || value;
},
REVIVER_PROTOTYPED = function(key, value, proto, parseProtoCheck, reviveDate) {
if (reviveDate && (typeof value==='string')) {
return value.toDate() || value;
}
if (!Object.isObject(value)) {
return value;
}
// only first level of objects can be given the specified prototype
if ((typeof parseProtoCheck === 'function') && !parseProtoCheck(value)) {
return value;
}
if (PROTO_SUPPORTED) {
value.__proto__ = proto;
return value;
}
return value.deepClone(null, proto);
},
MIME_JSON = 'application/json',
CONTENT_TYPE = 'Content-Type',
DELETE = 'delete',
REGEXP_ARRAY = /^( )*\[/,
REGEXP_OBJECT = /^( )*{/,
REGEXP_REMOVE_LAST_COMMA = /^(.*),( )*$/;
/*jshint proto:false */
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.IO_Transfer) {
return window._ITSAmodules.IO_Transfer; // IO_Transfer was already created
}
var IO = require('../io.js')(window),
/*
* Adds properties to the xhr-object: in case of streaming,
* xhr._parseStream=function is created to parse streamed data.
*
* @method _progressHandle
* @param xhr {Object} containing the xhr-instance
* @param props {Object} the propertie-object that is added too xhr and can be expanded
* @param options {Object} options of the request
* @private
*/
_entendXHR = function(xhr, props, options /*, promise */) {
var isarray, isobject, parialdata, regexpcomma, followingstream;
if ((typeof options.streamback === 'function') && options.headers && (options.headers.Accept==='application/json')) {
console.log(NAME, 'entendXHR');
xhr._parseStream = function(streamData) {
console.log(NAME, 'entendXHR --> _parseStream');
// first step is to determine if the final response would be an array or an object
// partial responses should be expanded to the same type
if (!followingstream) {
isarray = REGEXP_ARRAY.test(streamData);
isarray || (isobject = REGEXP_OBJECT.test(streamData));
}
try {
if (isarray || isobject) {
regexpcomma = streamData.match(REGEXP_REMOVE_LAST_COMMA);
parialdata = regexpcomma ? streamData.match(REGEXP_REMOVE_LAST_COMMA)[1] : streamData;
}
else {
parialdata = streamData;
}
parialdata = (followingstream && isarray ? '[' : '') + (followingstream && isobject ? '{' : '') + parialdata + (regexpcomma && isarray ? ']' : '') + (regexpcomma && isobject ? '}' : '');
// note: parsing will fail for the last streamed part, because there will be a double ] or }
streamData = JSON.parse(parialdata, (options.parseJSONDate) ? REVIVER : null);
}
catch(err) {
console.warn(NAME, err);
}
followingstream = true;
return streamData;
};
}
return xhr;
};
IO._xhrList.push(_entendXHR);
/**
* Performs an AJAX GET request. Shortcut for a call to [`xhr`](#method_xhr) with `method` set to `'GET'`.
* Additional parameters can be on the url (with questionmark), through `params`, or both.
*
* The Promise gets fulfilled if the server responses with `STATUS-CODE` in the 200-range (excluded 204).
* It will be rejected if a timeout occurs (see `options.timeout`), or if `xhr.abort()` gets invoked.
*
* Note: `params` should be a plain object with only primitive types which are transformed into key/value pairs.
*
* @method get
* @param url {String} URL of the resource server
* @param [params] {Object} additional parameters.
* should be a plain object with only primitive types which are transformed into key/value pairs.
* @param [options] {Object}
* @param [options.sync=false] {boolean} By default, all requests are sent asynchronously. To send synchronous requests, set to true.
* @param [options.headers] {Object} HTTP request headers.
* @param [options.responseType] {String} Force the response type.
* @param [options.timeout=3000] {Number} to timeout the request, leading into a rejected Promise.
* @param [options.withCredentials=false] {boolean} Whether or not to send credentials on the request.
* @return {Promise}
* on success:
* xhr {XMLHttpRequest|XDomainRequest} xhr-response
* on failure an Error object
* reason {Error}
*/
IO.get = function (url, options) {
console.log(NAME, 'get --> '+url);
var ioPromise, returnPromise;
options || (options={});
options.url = url;
options.method = 'GET';
// delete hidden property `data`: don't want accedentially to be used
delete options.data;
ioPromise = this.request(options);
returnPromise = ioPromise.then(
function(xhrResponse) {
return xhrResponse.responseText;
}
);
// set `abort` to the thennable-promise:
returnPromise.abort = ioPromise.abort;
return returnPromise;
};
/**
* Performs an AJAX request with the GET HTTP method and expects a JSON-object.
* The resolved Promise-callback returns an object (JSON-parsed serverresponse).
*
* Additional request-parameters can be on the url (with questionmark), through `params`, or both.
*
* The Promise gets fulfilled if the server responses with `STATUS-CODE` in the 200-range (excluded 204).
* It will be rejected if a timeout occurs (see `options.timeout`), or if `xhr.abort()` gets invoked.
*
* Note1: If you expect the server to response with data that consist of Date-properties, you should set `options.parseJSONDate` true.
* Parsing takes a bit longer, but it will generate trully Date-objects.
* Note2: CORS is supported, as long as the responseserver is set up to:
* a) has a response header which allows the clientdomain:
* header('Access-Control-Allow-Origin: http://www.some-site.com'); or header('Access-Control-Allow-Origin: *');
* b) in cae you have set a custom HEADER (through 'options'), the responseserver MUST listen and respond
* to requests with the OPTION-method
* More info: allows to send to your domain: see http://remysharp.com/2011/04/21/getting-cors-working/
*
* @method read
* @param url {String} URL of the resource server
* @param [params] {Object} additional parameters.
* @param [options] {Object} See also: [`I.io`](#method_xhr)
* can be ignored, even if streams are used --> the returned Promise will always hold all data
* @param [options.sync=false] {boolean} By default, all requests are sent asynchronously. To send synchronous requests, set to true.
* @param [options.headers] {Object} HTTP request headers.
* @param [options.timeout=3000] {Number} to timeout the request, leading into a rejected Promise.
* @param [options.withCredentials=false] {boolean} Whether or not to send credentials on the request.
* @param [options.parseJSONDate=false] {boolean} Whether the server returns JSON-stringified data which has Date-objects.
* @param [options.parseProto] {Object} to set the prototype of any object.
* @param [options.parseProtoCheck] {Function} to determine in what case the specified `parseProto` should be set as the prototype.
* The function accepts the `object` as argument and should return a trully value in order to set the prototype.
* When not specified, `parseProto` will always be applied (if `parseProto`is defined)
* @return {Promise}
* on success:
* Object received data
* on failure an Error object
* reason {Error}
*/
IO.read = function(url, params, options) {
console.log(NAME, 'read --> '+url+' params: '+JSON.stringify(params));
var ioPromise, returnPromise;
options || (options={});
options.headers || (options.headers={});
options.url = url;
options.method = 'GET';
options.data = params;
options.headers.Accept = 'application/json';
// we don't want the user to re-specify the server's responsetype:
delete options.responseType;
ioPromise = this.request(options);
returnPromise = ioPromise.then(
function(xhrResponse) {
// not 'try' 'catch', because, if parsing fails, we actually WANT the promise to be rejected
// we also need to re-attach the 'abort-handle'
console.log(NAME, 'read returns with: '+JSON.stringify(xhrResponse.responseText));
// xhrResponse.responseText should be 'application/json' --> if it is not,
// JSON.parse throws an error, but that's what we want: the Promise would reject
if (options.parseProto) {
return JSON.parse(xhrResponse.responseText, REVIVER_PROTOTYPED.rbind(null, options.parseProto, options.parseProtoCheck, options.parseJSONDate));
}
return JSON.parse(xhrResponse.responseText, (options.parseJSONDate) ? REVIVER : null);
}
);
// set `abort` to the thennable-promise:
returnPromise.abort = ioPromise.abort;
return returnPromise;
};
/**
* Sends data (object) which will be JSON-stringified before sending.
* Performs an AJAX request with the PUT HTTP method by default.
* When options.allfields is `false`, it will use the POST-method: see Note2.
*
* The 'content-type' of the header is set to 'application/json', overruling manually options.
*
* 'data' is send as 'body.data' and should be JSON-parsed at the server.
*
* The Promise gets fulfilled if the server responses with `STATUS-CODE` in the 200-range (excluded 204).
* It will be rejected if a timeout occurs (see `options.timeout`), or if `xhr.abort()` gets invoked.
*
* Note1: The server needs to inspect the bodyparam: 'action', which always equals 'update'.
* 'body.action' is the way to distinquish 'I.IO.updateObject' from 'I.IO.insertObject'.
* On purpose, we didn't make this distinction through a custom CONTENT-HEADER, because
* that would lead into a more complicated CORS-setup (see Note3)
* Note2: By default this method uses the PUT-request: which is preferable is you send the WHOLE object.
* if you send part of the fields, set `options.allfields`=false.
* This will lead into using the POST-method.
* More about HTTP-methods: https://stormpath.com/blog/put-or-post/
* Note3: CORS is supported, as long as the responseserver is set up to:
* a) has a response header which allows the clientdomain:
* header('Access-Control-Allow-Origin: http://www.some-site.com'); or header('Access-Control-Allow-Origin: *');
* b) in cae you have set a custom HEADER (through 'options'), the responseserver MUST listen and respond
* to requests with the OPTION-method
* More info: allows to send to your domain: see http://remysharp.com/2011/04/21/getting-cors-working/
* Note4: If the server response JSON-stringified data, the Promise resolves with a JS-Object. If you expect this object
* to consist of Date-properties, you should set `options.parseJSONDate` true. Parsing takes a bit longer, but it will
* generate trully Date-objects.
*
*
* @method update
* @param url {String} URL of the resource server
* @param data {Object|Promise} Data to be sent, might be a Promise which resolves with the data-object.
* @param [options] {Object} See also: [`I.io`](#method_xhr)
* @param [options.allfields=true] {boolean} to specify that all the object-fields are sent.
* @param [options.sync=false] {boolean} By default, all requests are sent asynchronously. To send synchronous requests, set to true.
* @param [options.headers] {Object} HTTP request headers.
* @param [options.timeout=3000] {Number} to timeout the request, leading into a rejected Promise.
* @param [options.withCredentials=false] {boolean} Whether or not to send credentials on the request.
* @param [options.parseJSONDate=false] {boolean} Whether the server returns JSON-stringified data which has Date-objects.
* @return {Promise}
* on success:
* response {Object} usually, the final object-data, possibly modified
* on failure an Error object
* reason {Error}
*/
/**
* Performs an AJAX request with the POST HTTP method by default.
* When options.allfields is `true`, it will use the PUT-method: see Note2.
* The send data is an object which will be JSON-stringified before sending.
*
* The 'content-type' of the header is set to 'application/json', overruling manually options.
*
* 'data' is send as 'body.data' and should be JSON-parsed at the server.
* 'body.action' has the value 'insert'
*
* The Promise gets fulfilled if the server responses with `STATUS-CODE` in the 200-range (excluded 204).
* It will be rejected if a timeout occurs (see `options.timeout`), or if `xhr.abort()` gets invoked.
*
* Note1: The server needs to inspect the bodyparam: 'action', which always equals 'insert'.
* 'body.action' is the way to distinquish 'I.IO.insertObject' from 'I.IO.updateObject'.
* On purpose, we didn't make this distinction through a custom CONTENT-HEADER, because
* that would lead into a more complicated CORS-setup (see Note3)
* Note2: By default this method uses the POST-request: which is preferable if you don't know all the fields (like its unique id).
* if you send ALL the fields, set `options.allfields`=true.
* This will lead into using the PUT-method.
* More about HTTP-methods: https://stormpath.com/blog/put-or-post/
* Note3: CORS is supported, as long as the responseserver is set up to:
* a) has a response header which allows the clientdomain:
* header('Access-Control-Allow-Origin: http://www.some-site.com'); or header('Access-Control-Allow-Origin: *');
* b) in cae you have set a custom HEADER (through 'options'), the responseserver MUST listen and respond
* to requests with the OPTION-method
* More info: allows to send to your domain: see http://remysharp.com/2011/04/21/getting-cors-working/
* Note4: If the server response JSON-stringified data, the Promise resolves with a JS-Object. If you expect this object
* to consist of Date-properties, you should set `options.parseJSONDate` true. Parsing takes a bit longer, but it will
* generate trully Date-objects.
*
* @method insert
* @param url {String} URL of the resource server
* @param data {Object|Promise} Data to be sent, might be a Promise which resolves with the data-object.
* @param [options] {Object} See also: [`I.io`](#method_xhr)
* @param [options.allfields=false] {boolean} to specify that all the object-fields are sent.
* @param [options.sync=false] {boolean} By default, all requests are sent asynchronously. To send synchronous requests, set to true.
* @param [options.headers] {Object} HTTP request headers.
* @param [options.timeout=3000] {Number} to timeout the request, leading into a rejected Promise.
* @param [options.withCredentials=false] {boolean} Whether or not to send credentials on the request.
* @param [options.parseJSONDate=false] {boolean} Whether the server returns JSON-stringified data which has Date-objects.
* @return {Promise}
* on success:
* response {Object} usually, the final object-data, possibly modified, holding the key
* on failure an Error object
* reason {Error}
*/
/**
* Performs an AJAX request with the PUT HTTP method by default.
* When options.allfields is `false`, it will use the POST-method: see Note2.
* The send data is an object which will be JSON-stringified before sending.
*
* The 'content-type' of the header is set to 'application/json', overruling manually options.
*
* 'data' is send as 'body.data' and should be JSON-parsed at the server.
*
* The Promise gets fulfilled if the server responses with `STATUS-CODE` in the 200-range (excluded 204).
* It will be rejected if a timeout occurs (see `options.timeout`), or if `xhr.abort()` gets invoked.
*
* Note1: By default this method uses the PUT-request: which is preferable is you send the WHOLE object.
* if you send part of the fields, set `options.allfields`=false.
* This will lead into using the POST-method.
* More about HTTP-methods: https://stormpath.com/blog/put-or-post/
* Note2: CORS is supported, as long as the responseserver is set up to:
* a) has a response header which allows the clientdomain:
* header('Access-Control-Allow-Origin: http://www.some-site.com'); or header('Access-Control-Allow-Origin: *');
* b) in cae you have set a custom HEADER (through 'options'), the responseserver MUST listen and respond
* to requests with the OPTION-method
* More info: allows to send to your domain: see http://remysharp.com/2011/04/21/getting-cors-working/
* Note3: If the server response JSON-stringified data, the Promise resolves with a JS-Object. If you expect this object
* to consist of Date-properties, you should set `options.parseJSONDate` true. Parsing takes a bit longer, but it will
* generate trully Date-objects.
*
* @method send
* @param url {String} URL of the resource server
* @param data {Object} Data to be sent.
* @param [options] {Object} See also: [`I.io`](#method_xhr)
* @param [options.allfields=true] {boolean} to specify that all the object-fields are sent.
* @param [options.sync=false] {boolean} By default, all requests are sent asynchronously. To send synchronous requests, set to true.
* @param [options.headers] {Object} HTTP request headers.
* @param [options.timeout=3000] {Number} to timeout the request, leading into a rejected Promise.
* @param [options.withCredentials=false] {boolean} Whether or not to send credentials on the request.
* @param [options.parseJSONDate=false] {boolean} Whether the server returns JSON-stringified data which has Date-objects.
* @return {Promise}
* on success:
* response {Object|String} any response you want the server to return.
If the server send back a JSON-stringified object,
it will be parsed to return as a full object
You could set `options.parseJSONDate` true, it you want ISO8601-dates to be parsed as trully Date-objects
* on failure an Error object
* reason {Error}
*/
['update', 'insert', 'send'].forEach(
function (verb) {
IO[verb] = function (url, data, options) {
console.log(NAME, verb+' --> '+url+' data: '+JSON.stringify(data));
var instance = this,
allfields, useallfields, parseJSONDate, ioPromise, returnPromise;
options || (options={});
allfields = options.allfields,
useallfields = (typeof allfields==='boolean') ? allfields : (verb!=='insert');
parseJSONDate = options.parseJSONDate;
options.url = url;
options.method = useallfields ? 'PUT' : 'POST';
options.data = data;
options.headers || (options.headers={});
options.headers[CONTENT_TYPE] = MIME_JSON;
parseJSONDate && (options.headers['X-JSONDate']="true");
if (verb!=='send') {
options.headers.Accept = 'application/json';
// set options.action
options.headers['X-Action'] = verb;
// we don't want the user to re-specify the server's responsetype:
delete options.responseType;
}
ioPromise = instance.request(options);
returnPromise = ioPromise.then(
function(xhrResponse) {
if (verb==='send') {
return xhrResponse.responseText;
}
// In case of `insert` or `update`
// xhrResponse.responseText should be 'application/json' --> if it is not,
// JSON.parse throws an error, but that's what we want: the Promise would reject
return JSON.parse(xhrResponse.responseText, parseJSONDate ? REVIVER : null);
}
);
// set `abort` to the thennable-promise:
returnPromise.abort = ioPromise.abort;
return returnPromise;
};
}
);
/**
* Performs an AJAX DELETE request. Shortcut for a call to [`xhr`](#method_xhr) with `method` set to `'DELETE'`.
*
* The Promise gets fulfilled if the server responses with `STATUS-CODE` in the 200-range (excluded 204).
* It will be rejected if a timeout occurs (see `options.timeout`), or if `xhr.abort()` gets invoked.
*
* Note: `data` should be a plain object with only primitive types which are transformed into key/value pairs.
*
* @method delete
* @param url {String} URL of the resource server
* @param deleteKey {Object} Indentification of the id that has to be deleted. Typically an object like: {id: 12}
* This object will be passed as the request params.
* @param [options] {Object}
* @param [options.url] {String} The url to which the request is sent.
* @param [options.sync=false] {boolean} By default, all requests are sent asynchronously. To send synchronous requests, set to true.
* @param [options.params] {Object} Data to be sent to the server.
* @param [options.body] {Object} The content for the request body for POST method.
* @param [options.headers] {Object} HTTP request headers.
* @param [options.timeout=3000] {Number} to timeout the request, leading into a rejected Promise.
* @param [options.withCredentials=false] {boolean} Whether or not to send credentials on the request.
* @param [options.parseJSONDate=false] {boolean} Whether the server returns JSON-stringified data which has Date-objects.
* @return {Promise}
* on success:
* response {Object|String} any response you want the server to return.
If the server send back a JSON-stringified object,
it will be parsed to return as a full object
You could set `options.parseJSONDate` true, it you want ISO8601-dates to be parsed as trully Date-objects
* on failure an Error object
* reason {Error}
*/
IO[DELETE] = function (url, deleteKey, options) {
console.log(NAME, 'delete --> '+url+' deleteKey: '+JSON.stringify(deleteKey));
var ioPromise, returnPromise;
options || (options={});
options.url = url;
// method will be uppercased by IO.xhr
options.method = DELETE;
options.data = deleteKey;
delete options.responseType;
ioPromise = this.request(options);
returnPromise = ioPromise.then(
function(xhrResponse) {
var response = xhrResponse.responseText;
try {
response = JSON.parse(response, (options.parseJSONDate) ? REVIVER : null);
}
catch(err) {}
return response;
}
);
// set `abort` to the thennable-promise:
returnPromise.abort = ioPromise.abort;
return returnPromise;
};
window._ITSAmodules.IO_Transfer = IO;
return IO;
};
},{"../io.js":3192,"js-ext/extra/hashmap.js":3212,"js-ext/lib/object.js":3217,"js-ext/lib/string.js":3219,"polyfill/polyfill-base.js":3231}],3191:[function(require,module,exports){
"use strict";
/**
* Extends io by adding the method `readXML` to it.
* Should be called using the provided `mergeInto`-method like this:
*
* @example
* var IO = require("io");
* var IOxml = require("io-xml");
* IOxml.mergeInto(IO);
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module io
* @submodule io-xml
* @class IO
* @since 0.0.1
*/
require('js-ext');
var NAME = '[io-xml]: ',
REGEXP_XML = /(?: )*(<\?xml (?:.)*\?>)(?: )*(<(?:\w)+>)/,
createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.IO_XML) {
return window._ITSAmodules.IO_XML; // IO_XML was already created
}
var IO = require('../io.js')(window),
/*
* Adds properties to the xhr-object: in case of streaming,
* xhr._parseStream=function is created to parse streamed data.
*
* @method _progressHandle
* @param xhr {Object} containing the xhr-instance
* @param props {Object} the propertie-object that is added too xhr and can be expanded
* @param options {Object} options of the request
* @private
*/
_entendXHR = function(xhr, props, options, promise) {
var parser, followingstream, regegexp_endcont, regexpxml, xmlstart, container, endcontainer;
if ((typeof options.streamback === 'function') && options.headers && (options.headers.Accept==='text/xml')) {
console.log(NAME, 'entendXHR');
parser = new window.DOMParser();
xhr._parseStream = function(streamData) {
var fragment, parialdata;
console.log(NAME, 'entendXHR --> _parseStream');
try {
if (!followingstream) {
regexpxml = streamData.match(REGEXP_XML);
if (regexpxml) {
xmlstart = regexpxml[1];
container = regexpxml[2];
endcontainer = '</'+container.substr(1);
regegexp_endcont = new RegExp('(.*)'+endcontainer+'( )*$');
}
}
parialdata = (followingstream ? xmlstart+container : '') + streamData;
regegexp_endcont.test(streamData) || (parialdata+=endcontainer);
fragment = parser.parseFromString(parialdata, 'text/xml');
followingstream = true;
return fragment;
}
catch(err) {
promise.reject(err);
}
};
}
return xhr;
};
IO._xhrList.push(_entendXHR);
/**
* Performs an AJAX request with the GET HTTP method and expects a JSON-object.
* The resolved Promise-callback returns an object (JSON-parsed serverresponse).
*
* Additional request-parameters can be on the url (with questionmark), through `params`, or both.
*
* The Promise gets fulfilled if the server responses with `STATUS-CODE` in the 200-range (excluded 204).
* It will be rejected if a timeout occurs (see `options.timeout`), or if `xhr.abort()` gets invoked.
*
* Note1: If you expect the server to response with data that consist of Date-properties, you should set `options.parseJSONDate` true.
* Parsing takes a bit longer, but it will generate trully Date-objects.
* Note2: CORS is supported, as long as the responseserver is set up to:
* a) has a response header which allows the clientdomain:
* header('Access-Control-Allow-Origin: http://www.some-site.com'); or header('Access-Control-Allow-Origin: *');
* b) in cae you have set a custom HEADER (through 'options'), the responseserver MUST listen and respond
* to requests with the OPTION-method
* More info: allows to send to your domain: see http://remysharp.com/2011/04/21/getting-cors-working/
*
* @method readXML
* @param url {String} URL of the resource server
* @param [params] {Object} additional parameters.
* @param [options] {Object} See also: [`I.io`](#method_xhr)
* @param [options.url] {String} The url to which the request is sent.
* can be ignored, even if streams are used --> the returned Promise will always hold all data
* @param [options.sync=false] {boolean} By default, all requests are sent asynchronously. To send synchronous requests, set to true.
* @param [options.params] {Object} Data to be sent to the server.
* @param [options.body] {Object} The content for the request body for POST method.
* @param [options.headers] {Object} HTTP request headers.
* @param [options.responseType='text'] {String} The response type.
* @param [options.timeout=3000] {Number} to timeout the request, leading into a rejected Promise.
* @param [options.withCredentials=false] {boolean} Whether or not to send credentials on the request.
* @param [options.parseJSONDate=false] {boolean} Whether the server returns JSON-stringified data which has Date-objects.
* @return {Promise}
* on success:
* Object received data
* on failure an Error object
* reason {Error}
*/
IO.readXML = function(url, params, options) {
var XMLOptions = {
headers: {'Accept': 'text/xml'},
method: 'GET',
url: url,
data: params
},
ioPromise, returnPromise;
options && XMLOptions.merge(options);
ioPromise = this.request(XMLOptions);
returnPromise = ioPromise.then(
function(xhrResponse) {
// if the responsetype is no "text/xml", then throw an error, else return xhrResponse.responseXML;
// note that nodejs has "Content-Type" in lowercase!
// Also: XDR DOES NOT support getResponseHeader() --> so we must just assume the data is text/xml
var contenttype = !xhrResponse._isXDR && (xhrResponse.getResponseHeader('Content-Type') || xhrResponse.getResponseHeader('content-type'));
if (xhrResponse._isXDR || /^text\/xml/.test(contenttype)) {
// cautious: when streaming, xhrResponse.responseXML will be undefined in case of using XDR
return xhrResponse.responseXML || (new window.DOMParser()).parseFromString(xhrResponse.responseText, 'text/xml');
}
// when code comes here: no valid xml response:
throw new Error('recieved Content-Type is no XML');
}
);
// set `abort` to the thennable-promise:
returnPromise.abort = ioPromise.abort;
return returnPromise;
};
window._ITSAmodules.IO_XML = IO;
return IO;
};
},{"../io.js":3192,"js-ext":3213,"js-ext/extra/hashmap.js":3212}],3192:[function(require,module,exports){
/**
* Provides core IO-functionality.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module io
* @class IO
*/
"use strict";
require('polyfill/polyfill-base.js');
require('js-ext');
var NAME = '[io]: ',
GET = 'GET',
createHashMap = require('js-ext/extra/hashmap.js').createMap,
Event = require('event'),
async = require('utils').async,
DEF_REQ_TIMEOUT = 300000, // don't create an ever-lasting request: always quit after 5 minutes
BODY_METHODS = createHashMap({
POST: 1,
PUT: 1
}),
CONTENT_TYPE = 'Content-Type',
MIME_JSON = 'application/json',
DEF_CONTENT_TYPE_POST = 'application/x-www-form-urlencoded; charset=UTF-8',
ERROR_NO_XHR = 'no valid xhr transport-mechanism available',
REQUEST_TIMEOUT = 'Request-timeout',
UNKNOW_ERROR = 'Unknown response-error',
XHR_ERROR = 'XHR Error',
ABORTED = 'Request aborted',
NO_XHR = 'No valid xhr found on this browser';
module.exports = function (window) {
var ENCODE_URI_COMPONENT = encodeURIComponent,
IO;
// to prevent multiple IO instances
// (which might happen: http://nodejs.org/docs/latest/api/modules.html#modules_module_caching_caveats)
// we make sure IO is defined only once. Therefore we bind it to `window` and return it if created before
// We need a singleton IO, because submodules might merge in. You can't have them merging
// into some other IO-instance than which is used.
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
/*jshint boss:true */
if (IO=window._ITSAmodules.IO) {
/*jshint boss:false */
return IO; // IO was already created
}
IO = {
config: {},
//===============================================================================================
// private methods:
//===============================================================================================
_xhrList: [],
/**
* Initializes the xhr-instance, based on the config-params.
* This method is the standard way of doing xhr-requests without processing streams.
*
* @method _initXHR
* @param xhr {Object} xhr-instance
* @param options {Object}
* @param [options.url] {String} The url to which the request is sent.
* @param [options.method='GET'] {String} The HTTP method to use.
* can be ignored, even if streams are used --> the returned Promise will always hold all data
* @param [options.sync=false] {boolean} By default, all requests are sent asynchronously. To send synchronous requests, set to true.
* This feature only works in the browser: nodejs will always perform asynchronous requests.
* @param [options.data] {Object} Data to be sent to the server, either to be used by `query-params` or `body`.
* @param [options.headers] {Object} HTTP request headers.
* @param [options.responseType] {String} Force the response type.
* @param [options.timeout=3000] {number} to timeout the request, leading into a rejected Promise.
* @param [options.withCredentials=false] {boolean} Whether or not to send credentials on the request.
* @param fulfill {Function} reference to xhr-promise's fulfill-function
* @param reject {Function} reference to xhr-promise's reject-function
* @param promise {Promise} the xhr-promise which will be extended with the `abort()`-method
* @private
*/
_initXHR: function (xhr, options, promise) {
console.log(NAME, '_initXHR');
var instance = this,
url = options.url,
method = options.method || GET,
headers = options.headers || {}, // all request will get some headers
async = !options.sync,
data = options.data,
reject = promise.reject;
// xhr will be null in case of a CORS-request when no CORS is possible
if (!xhr) {
console.error(NAME, '_initXHR fails: '+ERROR_NO_XHR);
reject(new Error(ERROR_NO_XHR));
return;
}
console.log(NAME, '_initXHR succesfully created '+(xhr._isXHR2 ? 'XMLHttpRequest2' : (xhr._isXDR ? 'XDomainRequest' : 'XMLHttpRequest1'))+'-instance');
// method-name should be in uppercase:
method = method.toUpperCase();
// in case of BODY-method: eliminate any data behind querystring:
// else: append data-object behind querystring
if (BODY_METHODS[method]) {
url = url.split('?'); // now url is an array
url = url[0]; // now url is a String again
}
else if (data) {
url += ((url.indexOf('?') > 0) ? '&' : '?') + instance._toQueryString(data);
}
xhr.open(method, url, async);
// xhr.responseType = options.responseType || 'text';
options.withCredentials && (xhr.withCredentials=true);
// more initialisation might be needed by extended modules:
instance._xhrInitList.each(
function(fn) {
fn(xhr, promise, headers, method);
}
);
// send the request:
xhr.send((BODY_METHODS[method] && data) ? (((headers[CONTENT_TYPE]===MIME_JSON) || xhr._isXDR) ? JSON.stringify(data) : instance._toQueryString(data)) : null);
console.log(NAME, 'xhr send to '+url+' with method '+method);
// now add xhr.abort() to the promise, so we can call from within the returned promise-instance
promise.abort = function() {
console.log(NAME, 'xhr aborted');
reject(new Error(ABORTED));
xhr._aborted = true; // must be set: IE9 won't allow to read anything on xhr after being aborted
xhr.abort();
};
// in case synchronous transfer: force an xhr.onreadystatechange:
async || xhr.onreadystatechange();
},
/**
* Adds the `headers`-object to `xhr`-headers.
*
* @method _setHeaders
* @param xhr {Object} containing the xhr-instance
* @param headers {Object} containing all headers
* @param method {String} the request-method used
* @private
*/
_setHeaders: function(xhr, promise, headers, method) {
// XDR cannot set requestheaders, only XHR:
if (!xhr._isXDR) {
console.log(NAME, '_setHeaders');
var name;
if ((method!=='POST') && (method!=='PUT')) {
// force GET-request to make a request instead of using cache (like IE does):
headers['If-Modified-Since'] = 'Wed, 15 Nov 1995 01:00:00 GMT';
// header 'Content-Type' should only be set with POST or PUT requests:
delete headers[CONTENT_TYPE];
}
// set all headers
for (name in headers) {
xhr.setRequestHeader(name, headers[name]);
}
// in case of POST or PUT method: always make sure 'Content-Type' is specified
((method!=='POST') && (method!=='PUT')) || (headers && (CONTENT_TYPE in headers)) || xhr.setRequestHeader(CONTENT_TYPE, DEF_CONTENT_TYPE_POST);
}
},
/**
* Adds 2 methods on the xhr-instance which are used by xhr when events occur:
*
* xhr.onreadystatechange()
* xhr.ontimeout() // only XMLHttpRequest2
*
* These events are responsible for making the Promise resolve.
* @method _setReadyHandle
* @param xhr {Object} containing the xhr-instance
* @param fulfill {Function} reference to the Promise fulfill-function
* @param reject {Function} reference to the Promise reject-function
* @private
*/
_setReadyHandle: function(xhr, promise) {
console.log(NAME, '_setReadyHandle');
// for XDomainRequest, we need 'onload' instead of 'onreadystatechange'
xhr.onreadystatechange = function() {
// CANNOT console xhr.responseText here! IE9 will throw an error:
// you can only acces it after (xhr.readyState===4)
// also check xhr._aborted --> IE9 comes here after aborted and will throw an error when reading xhr's native properties
if (!xhr._aborted && (xhr.readyState===4)) {
clearTimeout(xhr._timer);
if ((xhr.status>=200) && (xhr.status<300)) {
console.log(NAME, 'xhr.onreadystatechange will fulfill xhr-instance: '+xhr.responseText);
// In case streamback function is set, but when no intermediate stream-data was send
// (or in case of XDR: below 2kb it doesn't call onprogress)
// --> we might need to call onprogress ourselve.
if (xhr._isStream && !xhr._gotstreamed) {
xhr.onprogress(xhr.responseText);
}
promise.fulfill(xhr);
}
else {
console.warn(NAME, 'xhr.onreadystatechange will reject xhr-instance: '+xhr.statusText);
promise.reject(new Error(xhr.statusText || UNKNOW_ERROR+' '+xhr.status));
}
}
};
xhr.onerror = function() {
clearTimeout(xhr._timer);
promise.reject(new Error(XHR_ERROR));
};
},
/**
* Stringifies an object into one string with every pair separated by `&`
*
* @method _toQueryString
* @param data {Object} containing key-value pairs
* @return {String} stringified presentation of the object, with every pair separated by `&`
* @private
*/
_toQueryString: function(data) {
var paramArray = [],
key, value;
// TODO: use `object` module
for (key in data) {
value = data[key];
key = ENCODE_URI_COMPONENT(key);
paramArray.push((value === null) ? key : (key + '=' + ENCODE_URI_COMPONENT(value)));
}
console.log(NAME, '_toQueryString --> '+paramArray.join('&'));
return paramArray.join('&');
},
/**
* Sends a HTTP request to the server and returns a Promise with an additional .abort() method to cancel the request.
* This method is the standard way of doing xhr-requests without processing streams.
*
* @method request
* @param options {Object}
* @param [options.url] {String} The url to which the request is sent.
* @param [options.method='GET'] {String} The HTTP method to use.
* can be ignored, even if streams are used --> the returned Promise will always hold all data
* @param [options.sync=false] {boolean} By default, all requests are sent asynchronously. To send synchronous requests, set to true.
* @param [options.data] {Object} Data to be sent to the server, either to be used by `query-params` or `body`.
* @param [options.headers] {Object} HTTP request headers.
* @param [options.responseType] {String} Force the response type.
* @param [options.timeout=3000] {number} to timeout the request, leading into a rejected Promise.
* @param [options.withCredentials=false] {boolean} Whether or not to send credentials on the request.
* @param [options.streamback] {Function} callbackfunction in case you want to process streams (needs io-stream module).
* @return {Promise} Promise holding the request. Has an additional .abort() method to cancel the request.
* <ul>
* <li>on success: xhr {XMLHttpRequest1|XMLHttpRequest2} xhr-response</li>
* <li>on failure: reason {Error}</li>
* </ul>
*/
request: function(options) {
console.log(NAME, 'request');
var instance = this,
props = {},
xhr, promise;
options || (options={});
promise = Promise.manage(options.streamback);
xhr = new window.XMLHttpRequest();
props._isXHR2 = ('withCredentials' in xhr) || (window.navigator.userAgent==='fake');
// it could be other modules like io-cors or io-stream have subscribed
// xhr might be changed, also private properties might be extended
instance._xhrList.each(
function(fn) {
xhr = fn(xhr, props, options, promise);
}
);
if (!xhr) {
return Promise.reject(NO_XHR);
}
xhr.merge(props);
console.log(NAME, 'request creating xhr of type: '+ (props._isXHR2 ? 'XMLHttpRequest2' : (props._isXDR ? 'XDomainRequest' : 'XMLHttpRequest1')));
console.log(NAME, 'CORS-IE: '+ props._CORS_IE + ', canStream: '+props._canStream);
// Don't use xhr.timeout --> IE<10 throws an error when set xhr.timeout
// We use a timer that aborts the request
Object.defineProperty(xhr, '_timer', {
configurable: false,
enumerable: false,
writable: false,
value: setTimeout(function() {
promise.reject(new Error(REQUEST_TIMEOUT));
xhr._aborted = true; // must be set: IE9 won't allow to read anything on xhr after being aborted
xhr.abort();
}, options.timeout || instance.config.timeout || DEF_REQ_TIMEOUT)
});
instance._initXHR(xhr, options, promise);
// to make any routine informed for the end of xhr, we emit an event
// to make sure they get informed after another routines have handled the response,
// we go async
promise.then(function() {
async(function() {
Event.emit(promise, 'IO:finish');
});
});
return promise;
}
};
IO._xhrInitList = [
IO._setReadyHandle,
IO._setHeaders
];
window._ITSAmodules.IO = IO;
return IO;
};
},{"event":3196,"js-ext":3213,"js-ext/extra/hashmap.js":3212,"polyfill/polyfill-base.js":3231,"utils":3232}],3193:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":3198,"js-ext/lib/object.js":3199,"polyfill/polyfill-base.js":3211}],3194:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":3193}],3195:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":3193,"js-ext/extra/classes.js":3197,"js-ext/lib/object.js":3199}],3196:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":3193,"./event-emitter.js":3194,"./event-listener.js":3195}],3197:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":3199,"js-ext/extra/hashmap.js":3198,"polyfill/polyfill-base.js":3202}],3198:[function(require,module,exports){
module.exports=require(4)
},{}],3199:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3198,"polyfill/polyfill-base.js":3202,"utils":3203}],3200:[function(require,module,exports){
module.exports=require(14)
},{}],3201:[function(require,module,exports){
module.exports=require(15)
},{}],3202:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3200,"./lib/window.console.js":3201}],3203:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3204,"./lib/timers.js":3205}],3204:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3198,"polyfill/polyfill-base.js":3208}],3205:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3208}],3206:[function(require,module,exports){
module.exports=require(14)
},{}],3207:[function(require,module,exports){
module.exports=require(15)
},{}],3208:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3206,"./lib/window.console.js":3207}],3209:[function(require,module,exports){
module.exports=require(14)
},{}],3210:[function(require,module,exports){
module.exports=require(15)
},{}],3211:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3209,"./lib/window.console.js":3210}],3212:[function(require,module,exports){
module.exports=require(4)
},{}],3213:[function(require,module,exports){
module.exports=require(202)
},{"./lib/array.js":3214,"./lib/function.js":3215,"./lib/json.js":3216,"./lib/object.js":3217,"./lib/promise.js":3218,"./lib/string.js":3219}],3214:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":3222}],3215:[function(require,module,exports){
module.exports=require(204)
},{"polyfill/polyfill-base.js":3222}],3216:[function(require,module,exports){
module.exports=require(205)
},{"polyfill/polyfill-base.js":3222}],3217:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3212,"polyfill/polyfill-base.js":3222,"utils":3223}],3218:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":3222}],3219:[function(require,module,exports){
module.exports=require(29)
},{}],3220:[function(require,module,exports){
module.exports=require(14)
},{}],3221:[function(require,module,exports){
module.exports=require(15)
},{}],3222:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3220,"./lib/window.console.js":3221}],3223:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3224,"./lib/timers.js":3225}],3224:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3212,"polyfill/polyfill-base.js":3228}],3225:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3228}],3226:[function(require,module,exports){
module.exports=require(14)
},{}],3227:[function(require,module,exports){
module.exports=require(15)
},{}],3228:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3226,"./lib/window.console.js":3227}],3229:[function(require,module,exports){
module.exports=require(14)
},{}],3230:[function(require,module,exports){
module.exports=require(15)
},{}],3231:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3229,"./lib/window.console.js":3230}],3232:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3233,"./lib/timers.js":3234}],3233:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3212,"polyfill/polyfill-base.js":3237}],3234:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3237}],3235:[function(require,module,exports){
module.exports=require(14)
},{}],3236:[function(require,module,exports){
module.exports=require(15)
},{}],3237:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3235,"./lib/window.console.js":3236}],3238:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":3246,"js-ext/extra/hashmap.js":3239,"polyfill/polyfill-base.js":3252}],3239:[function(require,module,exports){
module.exports=require(4)
},{}],3240:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":3243,"../lib/object.js":3246,"./classes.js":3238,"js-ext/extra/hashmap.js":3239,"polyfill/lib/weakmap.js":3250}],3241:[function(require,module,exports){
"use strict";
var createHashMap = require('./hashmap.js').createMap;
module.exports = createHashMap({
'abstract': true,
'arguments': true,
'assert': true,
'await': true,
'boolean': true,
'break': true,
'byte': true,
'case': true,
'catch': true,
'char': true,
'class': true,
'const': true,
'continue': true,
'debugger': true,
'default': true,
'delete': true,
'do': true,
'double': true,
'else': true,
'enum': true,
'eval': true,
'export': true,
'extends': true,
'false': true,
'final': true,
'finally': true,
'float': true,
'for': true,
'function': true,
'goto': true,
'if': true,
'import': true,
'implements': true,
'in': true,
'instanceof': true,
'int': true,
'interface': true,
'let': true,
'long': true,
'native': true,
'new': true,
'null': true,
'package': true,
'private': true,
'protected': true,
'public': true,
'return': true,
'short': true,
'static': true,
'strictfp': true,
'super': true,
'switch': true,
'synchronized': true,
'this': true,
'throw': true,
'throws': true,
'transient': true,
'true': true,
'try': true,
'typeof': true,
'var': true,
'void': true,
'volatile': true,
'while': true,
'with': true,
'yield': true
});
},{"./hashmap.js":3239}],3242:[function(require,module,exports){
"use strict";
require('./lib/function.js');
require('./lib/object.js');
require('./lib/string.js');
require('./lib/array.js');
require('./lib/json.js');
require('./lib/promise.js');
module.exports = {
createHashMap: require('./extra/hashmap.js').createMap,
Classes: require('./extra/classes.js'),
LightMap: require('./extra/lightmap.js')
};
},{"./extra/classes.js":3238,"./extra/hashmap.js":3239,"./extra/lightmap.js":3240,"./lib/array.js":3243,"./lib/function.js":3244,"./lib/json.js":3245,"./lib/object.js":3246,"./lib/promise.js":3247,"./lib/string.js":3248}],3243:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":3252}],3244:[function(require,module,exports){
module.exports=require(204)
},{"polyfill/polyfill-base.js":3252}],3245:[function(require,module,exports){
module.exports=require(205)
},{"polyfill/polyfill-base.js":3252}],3246:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3239,"polyfill/polyfill-base.js":3252,"utils":3253}],3247:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":3252}],3248:[function(require,module,exports){
module.exports=require(29)
},{}],3249:[function(require,module,exports){
module.exports=require(14)
},{}],3250:[function(require,module,exports){
module.exports=require(57)
},{}],3251:[function(require,module,exports){
module.exports=require(15)
},{}],3252:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3249,"./lib/window.console.js":3251}],3253:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3254,"./lib/timers.js":3255}],3254:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3239,"polyfill/polyfill-base.js":3258}],3255:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3258}],3256:[function(require,module,exports){
module.exports=require(14)
},{}],3257:[function(require,module,exports){
module.exports=require(15)
},{}],3258:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3256,"./lib/window.console.js":3257}],3259:[function(require,module,exports){
module.exports=require(218)
},{"event":3263,"js-ext":3280,"js-ext/extra/hashmap.js":3279,"polyfill":3298,"utils":3299}],3260:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":3265,"js-ext/lib/object.js":3266,"polyfill/polyfill-base.js":3278}],3261:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":3260}],3262:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":3260,"js-ext/extra/classes.js":3264,"js-ext/lib/object.js":3266}],3263:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":3260,"./event-emitter.js":3261,"./event-listener.js":3262}],3264:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":3266,"js-ext/extra/hashmap.js":3265,"polyfill/polyfill-base.js":3269}],3265:[function(require,module,exports){
module.exports=require(4)
},{}],3266:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3265,"polyfill/polyfill-base.js":3269,"utils":3270}],3267:[function(require,module,exports){
module.exports=require(14)
},{}],3268:[function(require,module,exports){
module.exports=require(15)
},{}],3269:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3267,"./lib/window.console.js":3268}],3270:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3271,"./lib/timers.js":3272}],3271:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3265,"polyfill/polyfill-base.js":3275}],3272:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3275}],3273:[function(require,module,exports){
module.exports=require(14)
},{}],3274:[function(require,module,exports){
module.exports=require(15)
},{}],3275:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3273,"./lib/window.console.js":3274}],3276:[function(require,module,exports){
module.exports=require(14)
},{}],3277:[function(require,module,exports){
module.exports=require(15)
},{}],3278:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3276,"./lib/window.console.js":3277}],3279:[function(require,module,exports){
module.exports=require(4)
},{}],3280:[function(require,module,exports){
module.exports=require(202)
},{"./lib/array.js":3281,"./lib/function.js":3282,"./lib/json.js":3283,"./lib/object.js":3284,"./lib/promise.js":3285,"./lib/string.js":3286}],3281:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":3289}],3282:[function(require,module,exports){
module.exports=require(204)
},{"polyfill/polyfill-base.js":3289}],3283:[function(require,module,exports){
module.exports=require(205)
},{"polyfill/polyfill-base.js":3289}],3284:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3279,"polyfill/polyfill-base.js":3289,"utils":3290}],3285:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":3289}],3286:[function(require,module,exports){
module.exports=require(29)
},{}],3287:[function(require,module,exports){
module.exports=require(14)
},{}],3288:[function(require,module,exports){
module.exports=require(15)
},{}],3289:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3287,"./lib/window.console.js":3288}],3290:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3291,"./lib/timers.js":3292}],3291:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3279,"polyfill/polyfill-base.js":3295}],3292:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3295}],3293:[function(require,module,exports){
module.exports=require(14)
},{}],3294:[function(require,module,exports){
module.exports=require(15)
},{}],3295:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3293,"./lib/window.console.js":3294}],3296:[function(require,module,exports){
module.exports=require(14)
},{}],3297:[function(require,module,exports){
module.exports=require(15)
},{}],3298:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3296,"./lib/window.console.js":3297}],3299:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3300,"./lib/timers.js":3301}],3300:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3279,"polyfill/polyfill-base.js":3304}],3301:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3304}],3302:[function(require,module,exports){
module.exports=require(14)
},{}],3303:[function(require,module,exports){
module.exports=require(15)
},{}],3304:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3302,"./lib/window.console.js":3303}],3305:[function(require,module,exports){
arguments[4][5][0].apply(exports,arguments)
},{"event-dom":3306,"js-ext/extra/classes.js":3401,"js-ext/extra/hashmap.js":3402,"js-ext/lib/object.js":3403,"js-ext/lib/promise.js":3404,"js-ext/lib/string.js":3405,"polyfill":3417,"utils/lib/timers.js":3418,"vdom":3474}],3306:[function(require,module,exports){
"use strict";
/**
* Integrates DOM-events to event. more about DOM-events:
* http://www.smashingmagazine.com/2013/11/12/an-introduction-to-dom-events/
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @example
* Event = require('event-dom')(window);
*
* @module event
* @submodule event-dom
* @class Event
* @since 0.0.1
*/
var NAME = '[event-dom]: ',
Event = require('event'),
later = require('utils').later,
createHashMap = require('js-ext/extra/hashmap.js').createMap,
OUTSIDE = 'outside',
REGEXP_NODE_ID = /^#\S+$/,
REGEXP_EXTRACT_NODE_ID = /#(\S+)/,
REGEXP_UI_OUTSIDE = /^.+outside$/,
TIME_BTN_PRESSED = 200,
PURE_BUTTON_ACTIVE = 'pure-button-active',
UI = 'UI:',
NODE = 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
TAP = 'tap',
CLICK = 'click',
RIGHTCLICK = 'right'+CLICK,
CENTERCLICK = 'center'+CLICK,
EV_REMOVED = UI+NODE+REMOVE,
EV_INSERTED = UI+NODE+INSERT,
EV_CONTENT_CHANGE = UI+NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = UI+ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = UI+ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = UI+ATTRIBUTE+INSERT,
mutationEventsDefined = false,
NO_DEEP_SEARCH = {},
/*
* Internal hash containing all DOM-events that are listened for (at `document`).
*
* DOMEvents = {
* 'tap': callbackFn,
* 'mousemove': callbackFn,
* 'keypress': callbackFn
* }
*
* @property DOMEvents
* @default {}
* @type Object
* @private
* @since 0.0.1
*/
DOMEvents = {};
require('js-ext/lib/string.js');
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('polyfill/polyfill-base.js');
module.exports = function (window) {
var DOCUMENT = window.document,
_domSelToFunc, _evCallback, _findCurrentTargets, _preProcessor, _setupEvents, _setupMutationListener, _teardownMutationListener,
_setupDomListener, _teardownDomListener, SORT, _sortFunc, _sortFuncReversed, _getSubscribers, _selToFunc, MUTATION_EVENTS, preventClick;
require('vdom')(window);
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.EventDom) {
return Event; // Event was already extended
}
MUTATION_EVENTS = [EV_REMOVED, EV_INSERTED, EV_CONTENT_CHANGE, EV_ATTRIBUTE_REMOVED, , EV_ATTRIBUTE_CHANGED, EV_ATTRIBUTE_INSERTED];
console.warn('event-dom 14');
/*
* Transforms the selector to a valid function
*
* @method _selToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_selToFunc = function(customEvent, subscriber) {
Event._sellist.some(function(selFn) {
return selFn(customEvent, subscriber);
});
};
/*
* Creates a filterfunction out of a css-selector. To be used for catching any dom-element, without restrictions
* of any context (like Parcels can --> Parcel.Event uses _parcelSelToDom instead)
* On "non-outside" events, subscriber.t is set to the node that first matches the selector
* so it can be used to set as e.target in the final subscriber
*
* @method _domSelToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_domSelToFunc = function(customEvent, subscriber) {
// this stage is runned during subscription
var outsideEvent = REGEXP_UI_OUTSIDE.test(customEvent),
selector = subscriber.f,
context = subscriber.o,
vnode = subscriber.o.vnode,
isCustomElement = vnode && vnode.isItag,
visibleContent = isCustomElement && !vnode.domNode.contentHidden,
nodeid, byExactId, newTarget, deepSearch;
isCustomElement = false;
console.log(NAME, '_domSelToFunc type of selector = '+typeof selector);
// note: selector could still be a function: in case another subscriber
// already changed it.
if ((!selector && !isCustomElement) || (typeof selector === 'function')) {
subscriber.n || (subscriber.n = isCustomElement ? context : DOCUMENT);
return true;
}
selector || (selector='');
nodeid = selector.match(REGEXP_EXTRACT_NODE_ID);
nodeid ? (subscriber.nId=nodeid[1]) : (subscriber.n=isCustomElement ? context : DOCUMENT);
byExactId = REGEXP_NODE_ID.test(selector);
deepSearch = !NO_DEEP_SEARCH[customEvent];
// set the selector to `subscriber._s` so that e.currentTarget can calculate it:
subscriber._s = selector;
subscriber.f = function(e) {
// this stage is runned when the event happens
console.log(NAME, '_domSelToFunc inside filter. selector: '+selector);
var node = e.target,
vnode = node.vnode,
character1 = selector && selector.substr(1),
match = false;
if (!isCustomElement || visibleContent || context.contains(node)) {
if (selector==='') {
match = true;
}
else {
// e.target is the most deeply node in the dom-tree that caught the event
// our listener uses `selector` which might be a node higher up the tree.
// we will reset e.target to this node (if there is a match)
// note that e.currentTarget will always be `document` --> we're not interested in that
// also, we don't check for `node`, but for node.matchesSelector: the highest level `document`
// is not null, yet it doesn't have .matchesSelector so it would fail
// we DON'T want this for the focus and blur event!
if (deepSearch) {
if (vnode) {
// we go through the vdom
if (!vnode.removedFromDOM) {
while (vnode && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = vnode.domNode;
}
vnode = vnode.vParent;
}
}
}
else {
// we go through the dom
while (node.matchesSelector && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the dom');
match = byExactId ? (node.id===character1) : node.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = node;
}
node = node.parentNode;
}
}
}
else {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
if (vnode) {
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
}
else {
match = node.matchesSelector && (byExactId ? (node.id===character1) : node.matchesSelector(selector));
}
match && (e.sourceTarget=node);
}
}
}
if (outsideEvent && !match) {
// there is a match for the outside-event:
// we need to set e.sourceTarget and e.target:
newTarget = DOCUMENT.getElement(selector, true);
if (newTarget) {
e.sourceTarget = node;
subscriber.t = newTarget;
}
else {
// make return `false` because the selector is not in the dom
match = true;
}
}
console.log(NAME, '_domSelToFunc filter returns '+(!outsideEvent ? match : !match));
return !outsideEvent ? match : !match;
};
return true;
};
// at this point, we need to find out what are the current node-refs. whenever there is
// a filter that starts with `#` --> in those cases we have a bubble-chain, because the selector isn't
// set up with `document` at its root.
// we couldn't do this at time of subscribtion, for the nodes might not be there at that time.
// however, we only need to do this once: we store the value if we find them
// no problem when the nodes leave the dom later: the previous filter wouldn't pass
_findCurrentTargets = function(subscribers) {
console.log(NAME, '_findCurrentTargets');
subscribers.forEach(
function(subscriber) {
console.log(NAME, '_findCurrentTargets for single subscriber. nId: '+subscriber.nId);
subscriber.nId && (subscriber.n=DOCUMENT.getElementById(subscriber.nId, true));
}
);
};
/*
* Generates an event through our Event-system. Does the actual transportation from DOM-events
* into our Eventsystem. It also looks at the response of our Eventsystem: if our system
* halts or preventDefaults the customEvent, then the original DOM-event will be preventDefaulted.
*
* @method _evCallback
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_evCallback = function(e) {
console.log(NAME, '_evCallback');
var allSubscribers = Event._subs,
eType = e.type,
eventobject, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs, subsOutside,
subscribers, eventobjectOutside, wildcard_named_subsOutside, customEvent, eventName, which;
eventName = eType;
// first: a `click` event might be needed to transformed into `rightclick`:
if (eventName===CLICK) {
which = e.which;
(which===2) && (eventName=CENTERCLICK);
(which===3) && (eventName=RIGHTCLICK);
}
if (eventName==='tap') {
// prevent the next click-event
preventClick = true;
e.clientX || (e.clientX = e.center && e.center.x);
e.clientY || (e.clientY = e.center && e.center.y);
}
else if (preventClick && (eventName===CLICK)) {
preventClick = false;
return;
}
if (eventName===CLICK) {
eventName = 'tap';
e.center = {
x: e.clientX,
y: e.clientY
};
e.eventType = 4;
e.pointerType = 'mouse';
e.tapCount = 1;
}
customEvent = 'UI:'+eventName;
subs = allSubscribers[customEvent];
wildcard_named_subs = allSubscribers['*:'+eventName];
named_wildcard_subs = allSubscribers['UI:*'];
wildcard_wildcard_subs = allSubscribers['*:*'];
// Emit the dom-event though our eventsystem:
// NOTE: emit() needs to be synchronous! otherwise we wouldn't be able
// to preventDefault in time
//
// e = eventobject from the DOM-event OR gesture-event
// eventobject = eventobject from our Eventsystem, which get returned by calling `emit()`
// now so the work:
subscribers = _getSubscribers(e, true, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
eventobject = Event._emit(e.target, customEvent, e, subscribers, [], _preProcessor, false, true);
// now check outside subscribers
subsOutside = allSubscribers[customEvent+OUTSIDE];
wildcard_named_subsOutside = allSubscribers['*:'+eventName+OUTSIDE];
subscribers = _getSubscribers(e, true, subsOutside, wildcard_named_subsOutside);
eventobjectOutside = Event._emit(e.target, customEvent+OUTSIDE, e, subscribers, [], _preProcessor, false, true);
// if eventobject was preventdefaulted or halted: take appropriate action on
// the original dom-event. Note: only the original event can caused this, not the outsideevent
// stopPropagation on the original eventobject has no impact on our eventsystem, but who know who else is watching...
// be carefull though: not all gesture events have e.stopPropagation
eventobject.status.halted && e.stopPropagation && e.stopPropagation();
// now we might need to preventDefault the original event.
// be carefull though: not all gesture events have e.preventDefault
if ((eventobject.status.halted || eventobject.status.defaultPrevented || eventobject.status.defaultPreventedContinue) && e.preventDefault) {
e.preventDefault();
}
if (eventobject.status.ok) {
// last step: invoke the aftersubscribers
// we need to do this asynchronous: this way we pass them AFTER the DOM-event's defaultFn
// also make sure to paas-in the payload of the manipulated eventobject
subscribers = _getSubscribers(e, false, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent, eventobject, [], subscribers, _preProcessor, true), 10);
// now check outside subscribers
subscribers = _getSubscribers(e, false, subsOutside, wildcard_named_subsOutside);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent+OUTSIDE, eventobjectOutside, [], subscribers, _preProcessor, true), 10);
}
};
/*
* Creates an array of subscribers in the right order, conform their position in the DOM.
* Only subscribers that match the filter are involved.
*
* @method _getSubscribers
* @param e {Object} eventobject
* @param before {Boolean} whether it is a before- or after-subscriber
* @param subs {Array} array with subscribers
* @param wildcard_named_subs {Array} array with subscribers
* @param named_wildcard_subs {Array} array with subscribers
* @param wildcard_wildcard_subs {Array} array with subscribers
* @private
* @since 0.0.1
*/
_getSubscribers = function(e, before, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs) {
var subscribers = [],
beforeOrAfter = before ? 'b' : 'a',
saveConcat = function(extrasubs) {
extrasubs && extrasubs[beforeOrAfter] && (subscribers=subscribers.concat(extrasubs[beforeOrAfter]));
};
saveConcat(subs);
saveConcat(wildcard_named_subs);
saveConcat(named_wildcard_subs);
saveConcat(wildcard_wildcard_subs);
if (subscribers.length>0) {
subscribers = function(array, testFunc) {
// quickest way to filter an array: see http://jsperf.com/array-filter-performance/4
var filtered = array.slice(0), i;
for (i=array.length-1; i>=0; i--) {
console.log(NAME, 'filtercheck for subscriber');
testFunc(array[i]) || filtered.splice(i, 1);
}
return filtered;
}(subscribers, function(subscriber) {return (!subscriber.f || subscriber.f.call(subscriber.o, e));});
if (subscribers.length>0) {
// _findCurrentTargets(subscribers);
// sorting, based upon the sortFn
subscribers.sort(SORT);
}
}
return subscribers;
};
/*
* Sets e.target and e.sourceTarget for the single subscriber.
* Needs to be done for evenry single subscriber, because with a single event, these values change for each subscriber
*
* @method _preProcessor
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_preProcessor = function(subscriber, e) {
console.log(NAME, '_preProcessor');
// inside the aftersubscribers, we may need exit right away.
// this would be the case whenever stopPropagation or stopImmediatePropagation was called
// in case the subscribernode equals the node on which stopImmediatePropagation was called: return true
var propagationStopped, immediatePropagationStopped,
targetnode = (subscriber.t || subscriber.n);
immediatePropagationStopped = e.status.immediatePropagationStopped;
if (immediatePropagationStopped && ((immediatePropagationStopped===targetnode) || !immediatePropagationStopped.contains(targetnode))) {
console.log(NAME, '_preProcessor will return true because of immediatePropagationStopped');
return true;
}
// in case the subscribernode does not fall within or equals the node on which stopPropagation was called: return true
propagationStopped = e.status.propagationStopped;
if (propagationStopped && (propagationStopped!==targetnode) && !propagationStopped.contains(targetnode)) {
console.log(NAME, '_preProcessor will return true because of propagationStopped');
return true;
}
e._s = subscriber._s;
// now we might need to set e.target to the right node:
// the filterfunction might have found the true domnode that should act as e.target
// and set it at subscriber.t
// also, we need to backup the original e.target: this one should be reset when
// we encounter a subscriber with its own filterfunction instead of selector
if (subscriber.t) {
e.sourceTarget || (e.sourceTarget=e.target);
e.target = subscriber.t;
}
else {
e.sourceTarget && (e.target=e.sourceTarget);
}
return false;
};
/*
* Transports DOM-events to the Event-system. Catches events at their most early stage:
* their capture-phase. When these events happen, a new customEvent is generated by our own
* Eventsystem, by calling _evCallback(). This way we keep DOM-events and our Eventsystem completely separated.
*
* @method _setupDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_setupDomListener = function(customEvent, subscriber) {
console.log(NAME, '_setupDomListener');
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0],
eventName = eventSplitted[1],
outsideEvent = REGEXP_UI_OUTSIDE.test(eventName);
// be careful: anyone could also register an `outside`-event.
// in those cases, the DOM-listener must be set up without `outside`
outsideEvent && (eventName=eventName.substring(0, eventName.length-7));
// if eventName equals `mouseover` or `mouseleave` then we quit:
// people should use `mouseover` and `mouseout`
if ((eventName==='mouseenter') || (eventName==='mouseleave')) {
console.warn(NAME, 'Subscription to '+eventName+' not supported, use mouseover and mouseout: this eventsystem uses these non-noisy so they act as mouseenter and mouseleave');
return;
}
// only accept tap-events, yet later on we WILL need to listen for click-events
(eventName===CLICK) && (eventName=TAP);
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(emitterName+':'+eventName+(outsideEvent ? OUTSIDE : ''), subscriber);
// already registered? then return, also return if someone registered for UI:*
if (DOMEvents[eventName] || (eventName==='*')) {
// cautious: one might have registered the event, but not yet the outsideevent.
// in that case: save this setting:
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
return;
}
DOMEvents[eventName] = true;
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
// one exception: windowresize should listen to the window-object
if (eventName==='resize') {
window.addEventListener(eventName, _evCallback);
}
else {
((eventName===RIGHTCLICK) || (eventName===CENTERCLICK)) && (eventName=TAP);
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.addEventListener(eventName, _evCallback, true);
// listen for both `tap` and `click` events to happen
(eventName===TAP) && DOCUMENT.addEventListener(CLICK, _evCallback, true);
}
};
_setupEvents = function() {
var lastFocussed;
// make sure disabled buttons don't work:
Event.before(['tap', 'press'], function(e) {
e.preventDefault();
}, '.pure-button-disabled, button[disabled]');
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.before(
'keydown',
function(e) {
e._buttonPressed = true;
Event.emit(e.target, 'UI:tap', e);
},
function(e) {
var keyCode = e.keyCode;
return (e.target.getTagName()==='BUTTON') && ((keyCode===13) || (keyCode===32));
}
);
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.after(
'tap',
function(e) {
var buttonNode = e.target;
if (e._buttonPressed) {
buttonNode.setClass(PURE_BUTTON_ACTIVE);
e._noRender = true;
// even if the node isn't in the DOM, we can still try to manipulate it:
// the vdom makes sure no errors occur when the node is already removed
later(buttonNode.removeClass.bind(buttonNode, PURE_BUTTON_ACTIVE), TIME_BTN_PRESSED);
}
}
);
// fix activeElement on Mac
Event.before('focus', function(e) {
// will come here more often because of bubblechain
// however, the last pass-through will set the deepest node
lastFocussed = e.target;
});
// patching DOCUMENT.activeElement because it doesn't work well in a Mac: https://developer.mozilla.org/en-US/docs/Web/API/document.activeElement
// DOCUMENT._activeElement is used with the patch for DOCUMENT.activeElement its getter
Event.after('focus', function() {
DOCUMENT._activeElement = lastFocussed;
});
Event.before('blur', function() {
DOCUMENT._activeElement = null;
});
// Note: window.document has no prototype
Object.defineProperty(DOCUMENT, 'activeElement', {
get: function() {
return DOCUMENT._activeElement || DOCUMENT.body;
}
});
};
_setupMutationListener = function() {
DOCUMENT.hasMutationSubs = true;
if (!mutationEventsDefined) {
Event.defineEvent(EV_REMOVED).unPreventable();
Event.defineEvent(EV_INSERTED).unPreventable();
Event.defineEvent(EV_CONTENT_CHANGE).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_REMOVED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_CHANGED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_INSERTED).unPreventable();
mutationEventsDefined = true;
}
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFunc = function(subscriberOne, subscriberTwo) {
return (subscriberTwo.t || subscriberTwo.n).contains(subscriberOne.t || subscriberOne.n) ? -1 : 1;
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFuncReversed = function(subscriberOne, subscriberTwo) {
return (subscriberOne.t || subscriberOne.n).contains(subscriberTwo.t || subscriberTwo.n) ? 1 : -1;
};
/*
* Removes DOM-eventsubscribers from document when they are no longer needed.
*
* @method _teardownDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @private
* @since 0.0.2
*/
_teardownDomListener = function(customEvent) {
var customEventWithoutOutside = customEvent.endsWith(OUTSIDE) ? customEvent.substr(0, customEvent.length-7) : customEvent,
eventSplitted = customEventWithoutOutside.split(':'),
eventName = eventSplitted[1],
stillInUse;
if ((customEventWithoutOutside===CLICK) || (customEventWithoutOutside===RIGHTCLICK) || (customEventWithoutOutside===CENTERCLICK)) {
stillInUse = Event._subs[CLICK] ||
Event._subs[CLICK+OUTSIDE];
Event._subs[RIGHTCLICK] ||
Event._subs[RIGHTCLICK+OUTSIDE],
Event._subs[CENTERCLICK] ||
Event._subs[CENTERCLICK+OUTSIDE];
eventName = CLICK;
}
else {
stillInUse = Event._subs[customEventWithoutOutside] || Event._subs[customEventWithoutOutside+OUTSIDE];
}
if (!stillInUse) {
console.log(NAME, '_teardownDomListener '+customEvent);
// remove eventlistener from `document`
// one exeption: windowresize should listen to the window-object
if (eventName==='resize') {
window.removeEventListener(eventName, _evCallback);
}
else {
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.removeEventListener(eventName, _evCallback, true);
}
delete DOMEvents[eventName];
}
};
_teardownMutationListener = function() {
if (!Event._subs[EV_REMOVED] &&
!Event._subs[EV_INSERTED] &&
!Event._subs[EV_CONTENT_CHANGE] &&
!Event._subs[EV_ATTRIBUTE_REMOVED] &&
!Event._subs[EV_ATTRIBUTE_CHANGED] &&
!Event._subs[EV_ATTRIBUTE_INSERTED]
) {
DOCUMENT.hasMutationSubs = false;
}
};
// Now a very tricky one:
// Some browsers do an array.sort down-top instead of top-down.
// In those cases we need another sortFn, for the position on an equal match should fall
// behind instead of before (which is the case on top-down sort)
[1,2].sort(function(a /*, b */) {
SORT || (SORT=(a===2) ? _sortFuncReversed : _sortFunc);
});
// Now we do some initialization in order to make DOM-events work:
Object.defineProperty(Event._defaultEventObj, 'currentTarget', {
get: function() {
var ev = this,
e_target = ev.target;
if (e_target && ev._s) {
if (e_target.matches(ev._s)) {
return e_target;
}
return e_target.inside(ev._s) || undefined;
}
}
});
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(UI+'*', _setupDomListener, Event)
._setEventObjProperty('stopPropagation', function() {this.status.ok || (this.status.propagationStopped = this.target);})
._setEventObjProperty('stopImmediatePropagation', function() {this.status.ok || (this.status.immediatePropagationStopped = this.target);});
// Notify when someone subscribes to any event at all --> we might need to transform the filterFn from a selector into a true fnction
// this is already done automaticly by _setupDomListener fo UI:* events
Event.notify('*:*', function(customEvent, subscriber) {
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0];
if ((emitterName!=='UI') && (typeof subscriber.f==='string')) {
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(customEvent, subscriber);
}
}, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(UI+'*', _teardownDomListener, Event);
Event._sellist = [_domSelToFunc];
_setupEvents();
// making HTMLElement to be able to emit using event-emitter:
(function(HTMLElementPrototype) {
HTMLElementPrototype.merge(Event.Emitter('UI'));
}(window.HTMLElement.prototype));
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(MUTATION_EVENTS, _setupMutationListener, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(MUTATION_EVENTS, _teardownMutationListener, Event);
// Note: window.document has no prototype
DOCUMENT.suppressMutationEvents = function(suppress) {
this._suppressMutationEvents = suppress;
};
// Event.noDeepDomEvt and Event._domCallback are the only method that is added to Event.
// We need to do this, because `event-mobile` needs access to the same method.
// We could have done without this method and instead listen for a custom-event to handle
// Mobile events, however, that would lead into 2 eventcycli which isn't performant.
/**
*
* @method noDeepDomEvt
* @param domEvent {String} the eventName that should be processed without deepsearch
* @param e {Object} eventobject
* @for Event
* @chainable
* @since 0.0.1
*/
Event.noDeepDomEvt = function(domEvent) {
domEvent.contains(':') || (domEvent=UI+domEvent);
NO_DEEP_SEARCH[domEvent] = true;
return this;
};
/**
* Does the actual transportation from DOM-events into the Eventsystem. It also looks at the response of
* the Eventsystem: on e.halt() or e.preventDefault(), the original DOM-event will be preventDefaulted.
*
* @method _domCallback
* @param eventName {String} the customEvent that is transported to the eventsystem
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
Event._domCallback = function(e) {
_evCallback(e);
};
// store module:
window._ITSAmodules.EventDom = Event;
return Event;
};
},{"event":3310,"js-ext/extra/hashmap.js":3326,"js-ext/lib/array.js":3327,"js-ext/lib/object.js":3328,"js-ext/lib/string.js":3329,"polyfill/polyfill-base.js":3341,"utils":3342,"vdom":3400}],3307:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":3312,"js-ext/lib/object.js":3313,"polyfill/polyfill-base.js":3325}],3308:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":3307}],3309:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":3307,"js-ext/extra/classes.js":3311,"js-ext/lib/object.js":3313}],3310:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":3307,"./event-emitter.js":3308,"./event-listener.js":3309}],3311:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":3313,"js-ext/extra/hashmap.js":3312,"polyfill/polyfill-base.js":3316}],3312:[function(require,module,exports){
module.exports=require(4)
},{}],3313:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3312,"polyfill/polyfill-base.js":3316,"utils":3317}],3314:[function(require,module,exports){
module.exports=require(14)
},{}],3315:[function(require,module,exports){
module.exports=require(15)
},{}],3316:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3314,"./lib/window.console.js":3315}],3317:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3318,"./lib/timers.js":3319}],3318:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3312,"polyfill/polyfill-base.js":3322}],3319:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3322}],3320:[function(require,module,exports){
module.exports=require(14)
},{}],3321:[function(require,module,exports){
module.exports=require(15)
},{}],3322:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3320,"./lib/window.console.js":3321}],3323:[function(require,module,exports){
module.exports=require(14)
},{}],3324:[function(require,module,exports){
module.exports=require(15)
},{}],3325:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3323,"./lib/window.console.js":3324}],3326:[function(require,module,exports){
module.exports=require(4)
},{}],3327:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":3332}],3328:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3326,"polyfill/polyfill-base.js":3332,"utils":3333}],3329:[function(require,module,exports){
module.exports=require(29)
},{}],3330:[function(require,module,exports){
module.exports=require(14)
},{}],3331:[function(require,module,exports){
module.exports=require(15)
},{}],3332:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3330,"./lib/window.console.js":3331}],3333:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3334,"./lib/timers.js":3335}],3334:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3326,"polyfill/polyfill-base.js":3338}],3335:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3338}],3336:[function(require,module,exports){
module.exports=require(14)
},{}],3337:[function(require,module,exports){
module.exports=require(15)
},{}],3338:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3336,"./lib/window.console.js":3337}],3339:[function(require,module,exports){
module.exports=require(14)
},{}],3340:[function(require,module,exports){
module.exports=require(15)
},{}],3341:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3339,"./lib/window.console.js":3340}],3342:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3343,"./lib/timers.js":3344}],3343:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3326,"polyfill/polyfill-base.js":3347}],3344:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3347}],3345:[function(require,module,exports){
module.exports=require(14)
},{}],3346:[function(require,module,exports){
module.exports=require(15)
},{}],3347:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3345,"./lib/window.console.js":3346}],3348:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],3349:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":3353,"js-ext/extra/hashmap.js":3350,"polyfill/polyfill-base.js":3359}],3350:[function(require,module,exports){
module.exports=require(4)
},{}],3351:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":3352,"../lib/object.js":3353,"./classes.js":3349,"js-ext/extra/hashmap.js":3350,"polyfill/lib/weakmap.js":3357}],3352:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":3359}],3353:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3350,"polyfill/polyfill-base.js":3359,"utils":3360}],3354:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":3359}],3355:[function(require,module,exports){
module.exports=require(29)
},{}],3356:[function(require,module,exports){
module.exports=require(14)
},{}],3357:[function(require,module,exports){
module.exports=require(57)
},{}],3358:[function(require,module,exports){
module.exports=require(15)
},{}],3359:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3356,"./lib/window.console.js":3358}],3360:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3361,"./lib/timers.js":3362}],3361:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3350,"polyfill/polyfill-base.js":3365}],3362:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3365}],3363:[function(require,module,exports){
module.exports=require(14)
},{}],3364:[function(require,module,exports){
module.exports=require(15)
},{}],3365:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3363,"./lib/window.console.js":3364}],3366:[function(require,module,exports){
module.exports=require(66)
},{}],3367:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":3366}],3368:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":3366}],3369:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":3366}],3370:[function(require,module,exports){
module.exports=require(14)
},{}],3371:[function(require,module,exports){
module.exports=require(15)
},{}],3372:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3370,"./lib/window.console.js":3371}],3373:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3374,"./lib/timers.js":3375}],3374:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3350,"polyfill/polyfill-base.js":3378}],3375:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3378}],3376:[function(require,module,exports){
module.exports=require(14)
},{}],3377:[function(require,module,exports){
module.exports=require(15)
},{}],3378:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3376,"./lib/window.console.js":3377}],3379:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":3380}],3380:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":3381,"js-ext/lib/object.js":3382}],3381:[function(require,module,exports){
module.exports=require(4)
},{}],3382:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3381,"polyfill/polyfill-base.js":3385,"utils":3386}],3383:[function(require,module,exports){
module.exports=require(14)
},{}],3384:[function(require,module,exports){
module.exports=require(15)
},{}],3385:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3383,"./lib/window.console.js":3384}],3386:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3387,"./lib/timers.js":3388}],3387:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3381,"polyfill/polyfill-base.js":3391}],3388:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3391}],3389:[function(require,module,exports){
module.exports=require(14)
},{}],3390:[function(require,module,exports){
module.exports=require(15)
},{}],3391:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3389,"./lib/window.console.js":3390}],3392:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":3350,"js-ext/lib/object.js":3353,"js-ext/lib/string.js":3355,"polyfill":3372,"polyfill/extra/transition.js":3367,"polyfill/extra/vendorCSS.js":3369}],3393:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":3350,"js-ext/lib/object.js":3353,"polyfill":3372}],3394:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":3350,"js-ext/lib/object.js":3353,"js-ext/lib/string.js":3355,"polyfill":3372}],3395:[function(require,module,exports){
arguments[4][95][0].apply(exports,arguments)
},{"../css/element.css":3348,"./attribute-extractor.js":3392,"./element-array.js":3393,"./html-parser.js":3396,"./node-parser.js":3397,"./vdom-ns.js":3398,"./vnode.js":3399,"js-ext/extra/hashmap.js":3350,"js-ext/lib/object.js":3353,"js-ext/lib/promise.js":3354,"js-ext/lib/string.js":3355,"polyfill":3372,"polyfill/extra/transition.js":3367,"polyfill/extra/transitionend.js":3368,"polyfill/extra/vendorCSS.js":3369,"utils":3373,"window-ext":3379}],3396:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":3392,"./vdom-ns.js":3398,"js-ext/extra/hashmap.js":3350,"js-ext/lib/object.js":3353,"polyfill":3372}],3397:[function(require,module,exports){
arguments[4][97][0].apply(exports,arguments)
},{"./attribute-extractor.js":3392,"./vdom-ns.js":3398,"./vnode.js":3399,"js-ext/extra/hashmap.js":3350,"js-ext/lib/object.js":3353,"polyfill":3372}],3398:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":3350,"js-ext/lib/object.js":3353,"polyfill":3372}],3399:[function(require,module,exports){
"use strict";
/**
* Delivers the `vnode` prototype object, which is a virtualisation of an `Element` inside the Dom.
* These Elements work smoothless with the vdom (see ...).
*
* vnodes are much quicker to access and walk through than native dom-nodes. However, this is a module you don't need
* by itself: `Element`-types use these features under the hood.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* <br>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
*
* @module vdom
* @submodule vnode
* @class vnode
* @since 0.0.1
*/
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('js-ext/lib/string.js');
require('polyfill');
var createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.VNode) {
return window._ITSAmodules.VNode; // VNODE was already created
}
console.warn('VDOM 21');
var NS = require('./vdom-ns.js')(window),
extractor = require('./attribute-extractor.js')(window),
DOCUMENT = window.document,
LightMap = require('js-ext/extra/lightmap.js'),
MUTATION_EVENTS = new LightMap(),
BATCH_WILL_RUN = false,
xmlNS = NS.xmlNS,
nodeids = NS.nodeids,
htmlToVNodes = require('./html-parser.js')(window),
timers = require('utils/lib/timers.js'),
async = timers.async,
later = timers.later,
/*jshint proto:true */
PROTO_SUPPORTED = !!Object.__proto__,
/*jshint proto:false */
// cleanup memory after 1 minute: removed nodes SHOULD NOT be accessed afterwards
// because vnode would be recalculated and might be different from before
DESTROY_DELAY = 60000,
unescapeEntities = NS.UnescapeEntities,
NTH_CHILD_REGEXP = /^(?:(\d*)[n|N])([\+|\-](\d+))?$/, // an+b
STRING = 'string',
CLASS = 'class',
STYLE = 'style',
ID = 'id',
NODE= 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
EV_REMOVED = NODE+REMOVE,
EV_INSERTED = NODE+INSERT,
EV_CONTENT_CHANGE = NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = ATTRIBUTE+INSERT,
SPLIT_CHARACTER = createHashMap({
' ': true,
'>': true,
'+': true, // only select the element when it is immediately preceded by the former element
'~': true // only the element when it has the former element as a sibling. (just like `+`, but less strict)
}),
STORABLE_SPLIT_CHARACTER = createHashMap({
'>': true,
'+': true,
'~': true
}),
SIBLING_MATCH_CHARACTER = createHashMap({
'+': true,
'~': true
}),
ATTR_DETAIL_SPECIFIERS = createHashMap({
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to attribute-name end-tokens.
*
* @property END_ATTRIBUTENAME
* @default {
* '=': true,
* ']': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
END_ATTRIBUTENAME = createHashMap({
'=': true,
']': true,
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to different changes of Element nodeType changes.
*
* @property NODESWITCH
* @default {
* 1: {
* 1: 1,
* 3: 2,
* 8: 3
* },
* 3: {
* 1: 4,
* 3: 5,
* 8: 6
* },
* 8: {
* 1: 7,
* 3: 8,
* 8: 9
* }
* }
* @type Object
* @protected
* @since 0.0.1
*/
NODESWITCH = createHashMap({
1: createHashMap({
1: 1, // oldNodeType==Element, newNodeType==Element
3: 2, // oldNodeType==Element, newNodeType==TextNode
8: 3 // oldNodeType==Element, newNodeType==Comment
}),
3: createHashMap({
1: 4, // oldNodeType==TextNode, newNodeType==Element
3: 5, // oldNodeType==TextNode, newNodeType==TextNode
8: 6 // oldNodeType==TextNode, newNodeType==Comment
}),
8: createHashMap({
1: 7, // oldNodeType==Comment, newNodeType==Element
3: 8, // oldNodeType==Comment, newNodeType==TextNode
8: 9 // oldNodeType==Comment, newNodeType==Comment
})
}),
/**
* Object to gain quick access to selector start-tokens.
*
* @property SELECTOR_IDENTIFIERS
* @default {
* '#': 1,
* '.': 2,
* '[': 3
* }
* @type Object
* @protected
* @since 0.0.1
*/
SELECTOR_IDENTIFIERS = createHashMap({
'#': 1,
'.': 2,
'[': 3,
':': 4
}),
PSEUDO_FIRST_CHILD = ':first-child',
PSEUDO_FIRST_OF_TYPE = ':first-of-type',
PSEUDO_LAST_CHILD = ':last-child',
PSEUDO_LAST_OF_TYPE = ':last-of-type',
PSEUDO_NTH_CHILD = ':nth-child',
PSEUDO_NTH_LAST_CHILD = ':nth-last-child',
PSEUDO_NTH_LAST_OF_TYPE = ':nth-last-of-type',
PSEUDO_NTH_OF_TYPE = ':nth-of-type',
PSEUDO_ONLY_OF_TYPE = ':only-of-type',
PSEUDO_ONLY_CHILD = ':only-child',
/**
* Object to gain quick access to the selectors that required children
*
* @property PSEUDO_REQUIRED_CHILDREN
* @default {
* ':first-child': true,
* ':first-of-type': true,
* ':last-child': true,
* ':last-of-type': true,
* ':nth-child': true,
* ':nth-last-child': true,
* ':nth-last-of-type': true,
* ':nth-of-type': true,
* ':only-of-type': true,
* ':only-child': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
PSEUDO_REQUIRED_CHILDREN = createHashMap(),
_matchesSelectorItem, _matchesOneSelector, _findElementSibling, vNodeProto, _markRemoved, _tryReplaceChild,
_splitSelector, _findNodeSibling, _matchNthChild, _batchEmit, _emitDestroyChildren, _tryRemoveDomNode;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_CHILD] = true;
/**
* Searches for the next -or previous- node-sibling (nodeType of 1, 3 or 8).
*
* @method _findNodeSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findNodeSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
index = vParent.vChildNodes.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildNodes[index];
};
/**
* Searches for the next -or previous- Element-sibling (nodeType of 1).
*
* @method _findElementSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findElementSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
if (vnode.nodeType===1) {
index = vParent.vChildren.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildren[index];
}
else {
/*jshint noempty:true */
while ((vnode=_findNodeSibling(vnode, next)) && (vnode.nodeType!==1)) {}
/*jshint noempty:false */
return vnode;
}
};
/**
* Check whether the vnode matches a "nth-child" test, which is used for css pseudoselectors like `nth-child`, `nth-of-type` etc.
*
* @method _matchNthChild
* @param pseudoArg {String} the argument for nth-child
* @param index {Number} the index of the inspected vnode
* @return {Boolean} whether the vnode matches the nthChild test
* @protected
* @private
* @since 0.0.1
*/
_matchNthChild = function(pseudoArg, index) {
var match, k, a, b, nodeOk, nthIndex, sign, isNumber;
(pseudoArg==='even') && (pseudoArg='2n');
(pseudoArg==='odd') && (pseudoArg='2n+1');
match = pseudoArg.match(NTH_CHILD_REGEXP) || (isNumber=pseudoArg.validateNumber());
if (!match) {
return false;
}
// pseudoArg follows the pattern: `an+b`
if (!isNumber) {
a = match[1];
sign = match[2];
b = match[3] || 0;
(b==='') && (b=0);
}
else {
b = pseudoArg;
}
sign && (sign=sign[0]);
if (!a) {
// only fixed index to match
return (sign==='-') ? false : (parseInt(b, 10)===index);
}
else {
// we need to iterate
nodeOk = false;
b = window.Number(b);
for (k=0; !nodeOk; k++) {
nthIndex = (sign==='-') ? (a*k) - b : (a*k) + b;
if (nthIndex===index) {
nodeOk = true;
}
else if (nthIndex>index) {
// beyond index --> will never become a fix anymore
return false;
}
}
return nodeOk;
}
};
/**
* Check whether the vnode matches the css-selector. the css-selector should be a single selector,
* not multiple, so it shouldn't contain a `comma`.
*
* @method _matchesOneSelector
* @param vnode {vnode} the vnode to inspect
* @param selector {String} the selector-item to check the match for
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches the css-selector
* @protected
* @private
* @since 0.0.1
*/
_matchesOneSelector = function(vnode, selector, relatedVNode) {
var selList = _splitSelector(selector),
size = selList.length,
selMatch = false,
i, selectorItem, last,
rightvnode, relationMatch, checkRelation;
if (STORABLE_SPLIT_CHARACTER[selList[size-1]]) {
return false;
}
relationMatch = function(leftVNode, rightVNode, relationCharacter) {
var match, vParent, vChildren;
// when `selector` starts with `>`, `~` or `+`, then
// there should also be a match comparing a related node!
switch (relationCharacter) {
case '>':
leftVNode || (leftVNode=rightVNode.vParent);
match = (leftVNode.vChildren.indexOf(rightVNode)!==-1);
break;
case '~':
leftVNode || (leftVNode=rightVNode.vFirstElement);
vParent = leftVNode.vParent;
vChildren = vParent && vParent.vChildren;
match = vParent && (vChildren.indexOf(leftVNode)<vChildren.indexOf(rightVNode));
break;
case '+':
leftVNode || (leftVNode=rightVNode.vPreviousElement);
match = (leftVNode.vNextElement === rightVNode);
}
return !!match;
};
last = size - 1;
i = last;
while (vnode && ((selMatch || (i===last)) && (i>=0))) {
selectorItem = selList[i];
if (STORABLE_SPLIT_CHARACTER[selectorItem]) {
checkRelation = selectorItem;
i--;
}
else {
selMatch = _matchesSelectorItem(vnode, selectorItem);
if (!selMatch && (i===last)) {
return false;
}
while (!selMatch && vnode && (i!==last)) {
// may also look at nodes higher in the chain
vnode = SIBLING_MATCH_CHARACTER[selList[i+1]] ? vnode.vPreviousElement : vnode.vParent;
selMatch = vnode && _matchesSelectorItem(vnode, selectorItem);
}
selMatch && checkRelation && vnode && (selMatch=relationMatch(vnode, rightvnode, checkRelation));
rightvnode = vnode;
if (vnode) {
// do a precheck on the next checkRelation:
// and determine whether to set the vnode upper the tree or at the previous sibling:
vnode = SIBLING_MATCH_CHARACTER[selList[i-1]] ? vnode.vPreviousElement : vnode.vParent;
}
if (selMatch) {
checkRelation = false;
i--;
}
}
}
if ((rightvnode===relatedVNode) || (i!==-1)) {
selMatch = false;
}
selMatch && checkRelation && rightvnode && (selMatch=relationMatch(relatedVNode, rightvnode, checkRelation));
return selMatch;
};
/**
* Check whether the vnode matches one specific selector-item. Suppose the css-selector: "#mynode li.red .blue"
* then there are 3 selector-items: "#mynode", "li.red" and ".blue"
*
* This method also can handle the new selectors:
* <ul>
* <li>[att^=val] –-> the “begins with” selector</li>
* <li>[att$=val] –-> the “ends with” selector</li>
* <li>[att*=val] –-> the “contains” selector (might be a substring)</li>
* <li>[att~=val] –-> the “contains” selector as a separate word, separated by spaces</li>
* <li>[att|=val] –-> the “contains” selector as a separate word, separated by `|`</li>
* <li>+ --> (same level)</li>
* <li>~ --> (same level)</li>
* </ul>
*
* @method _matchesSelectorItem
* @param vnode {Object} the vnode to inspect
* @param selectorItem {String} the selector-item to check the match for
* @return {Boolean} whether the vnode matches the selector-item
* @protected
* @private
* @since 0.0.1
*/
_matchesSelectorItem = function (vnode, selectorItem) {
var i = 0,
len = selectorItem.length,
character = selectorItem[0],
tagName, id, className, attributeName, attributeValue, stringMarker, attributeisString, isBoolean, insideAttributeValue, insideAttribute,
vParent, checkBoolean, treatment, k, min, max, value, len2, index, found, pseudo, pseudoArg, arglevel, count, vParentVChildren;
if (selectorItem==='*') {
return true;
}
if (!SELECTOR_IDENTIFIERS[character]) {
// starts with tagName
tagName = '';
// reposition i to continue in the right way:
i--;
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
tagName += character;
}
if (tagName.toUpperCase()!==vnode.tag) {
return false;
}
}
while (i<len) {
switch (character) {
case '#':
id = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
id += character;
}
if (id!==vnode.id) {
return false;
}
break;
case '.':
className = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
className += character;
}
if (!vnode.hasClass(className)) {
return false;
}
break;
case '[':
attributeName = '';
while ((++i<len) && (character=selectorItem[i]) && !END_ATTRIBUTENAME[character]) {
attributeName += character;
}
// if character===']' then we have an attribute without a value-definition
if (!vnode.attrs[attributeName] || ((character===']') && (vnode.attrs[attributeName]!==''))) {
return !!vnode.attrs[attributeName];
}
// now we read the value of the attribute
// however, it could be that the selector has a special `detailed` identifier set (defined by: ATTR_DETAIL_SPECIFIERS)
if (ATTR_DETAIL_SPECIFIERS[character]) {
treatment = character; // store the character to know how the attributedata should be treaded
i++; // character should be a "=" by now
}
else {
treatment = null;
}
attributeValue = '';
stringMarker = selectorItem[i+1];
attributeisString = (stringMarker==='"') || (stringMarker==="'");
attributeisString && (i++);
// end of attributaValue = (character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker))
while ((++i<len) && (character=selectorItem[i]) && !((character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker)))) {
attributeValue += character;
}
if (attributeisString) {
// if attribute is string, then we need to _remove to last stringmarker
attributeValue = attributeValue.substr(0, attributeValue.length-1);
}
else {
// if attribute is no string, then we need to typecast its value
isBoolean = ((attributeValue.length>3) && (attributeValue.length<6) &&
(checkBoolean=attributeValue.toUpperCase()) &&
((checkBoolean==='FALSE') || (checkBoolean==='TRUE')));
// typecast the value to either Boolean or Number:
attributeValue = isBoolean ? (checkBoolean==='TRUE') : parseFloat(attributeValue);
}
// depending upon how the attributedata should be treated:
if (treatment) {
switch (treatment) {
case '^': // “begins with” selector
if (!vnode.attrs[attributeName].startsWith(attributeValue)) {
return false;
}
break;
case '$': // “ends with” selector
if (!vnode.attrs[attributeName].endsWith(attributeValue)) {
return false;
}
break;
case '*': // “contains” selector (might be a substring)
if (!vnode.attrs[attributeName].contains(attributeValue)) {
return false;
}
break;
case '~': // “contains” selector as a separate word, separated by spaces
if (!(' '+vnode.attrs[attributeName]+' ').contains(' '+attributeValue+' ')) {
return false;
}
break;
case '|': // “contains” selector as a separate word, separated by `|`
if (!('|'+vnode.attrs[attributeName]+'|').contains('|'+attributeValue+'|')) {
return false;
}
break;
}
}
else if (vnode.attrs[attributeName]!==attributeValue) {
return false;
}
// we still need to increase one position:
(++i<len) && (character=selectorItem[i]);
break;
case ':':
// we have a pseudo-selector
// first, find out which one
// because '::' is a valid start (though without any selection), we start to back the next character as well:
pseudo = ':'+selectorItem[++i];
pseudoArg = '';
vParent = vnode.vParent;
vParentVChildren = vParent && vParent.vChildren;
// pseudo-selectors might have an argument passed in, like `:nth-child(2n+1)` or `:not([type="checkbox"])` --> we
// store this argument inside `pseudoArg`
// also note that combinations are possible with `:not` --> `:not(:nth-child(2n+1))`
// also note that we cannot "just" look for a closing character when running into the usage of attributes:
// for example --> `:not([data-x="some data :)"])`
// that's why -once we are inside attribute-data- we need to continue until the attribute-data ends
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
if (character==='(') {
// starting arguments
arglevel = 1;
insideAttribute = false;
insideAttributeValue = false;
while ((++i<len) && (character=selectorItem[i]) && (arglevel>0)) {
if (!insideAttribute) {
if (character==='(') {
arglevel++;
}
else if (character===')') {
arglevel--;
}
else if (character==='[') {
insideAttribute = true;
}
}
else {
// inside attribute
if (!insideAttributeValue) {
if ((character==='"') || (character==="'")) {
insideAttributeValue = true;
stringMarker = character;
}
else if (character===']') {
insideAttribute = false;
}
}
else if ((character===stringMarker) && (selectorItem[i+1]===']')) {
insideAttributeValue = false;
}
}
(arglevel>0) && (pseudoArg+=character);
}
}
else {
pseudo += character;
}
}
// now, `pseudo` is known as well as its possible pseudoArg
if (!vParentVChildren && PSEUDO_REQUIRED_CHILDREN[pseudo]) {
return false;
}
switch (pseudo) {
case ':checked': // input:checked Selects every checked <input> element
if (!vnode.attrs.checked) {
return false;
}
break;
case ':disabled': // input:disabled Selects every disabled <input> element
if (!vnode.attrs.disabled) {
return false;
}
break;
case ':empty': // p:empty Selects every <p> element that has no children (including text nodes)
if (vnode.vChildNodes && (vnode.vChildNodes.length>0)) {
return false;
}
break;
case ':enabled': // input:enabled Selects every enabled <input> element
if (vnode.attrs.disabled) {
return false;
}
break;
case PSEUDO_FIRST_CHILD: // p:first-child Selects every <p> element that is the first child of its parent
if (vParentVChildren[0]!==vnode) {
return false;
}
break;
case PSEUDO_FIRST_OF_TYPE: // p:first-of-type Selects every <p> element that is the first <p> element of its parent
for (k=vParentVChildren.indexOf(vnode)-1; k>=0; k--) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':focus': // input:focus Selects the input element which has focus
if (vnode.domNode!==DOCUMENT.activeElement) {
return false;
}
break;
case ':in-range': // input:in-range Selects input elements with a value within a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || (value<min) || (value>max)) {
return false;
}
break;
case ':lang': // p:lang(it) Selects every <p> element with a lang attribute equal to "it" (Italian)
if (vnode.attrs.lang!==pseudoArg) {
return false;
}
break;
case PSEUDO_LAST_CHILD: // p:last-child Selects every <p> element that is the last child of its parent
if (vParentVChildren[vParentVChildren.length-1]!==vnode) {
return false;
}
break;
case PSEUDO_LAST_OF_TYPE: // p:last-of-type Selects every <p> element that is the last <p> element of its parent
len2 = vParentVChildren.length;
for (k=vParentVChildren.indexOf(vnode)+1; k<len2; k++) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':not': // :not(p) Selects every element that is not a <p> element
if (vnode.matchesSelector(pseudoArg)) {
return false;
}
break;
case PSEUDO_NTH_CHILD: // p:nth-child(2) Selects every <p> element that is the second child of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
index = vParentVChildren.indexOf(vnode)+1;
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_CHILD: // p:nth-last-child(2) Selects every <p> element that is the second child of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_OF_TYPE: // p:nth-last-of-type(2) Selects every <p> element that is the second <p> element of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
index = 0;
for (k=vParentVChildren.length-1; (k>=0) && !found; k--) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_OF_TYPE: // p:nth-of-type(2) Selects every <p> element that is the second <p> element of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
len2 = vParentVChildren.length;
index = 0;
for (k=0; (k<len2) && !found; k++) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_ONLY_OF_TYPE: // p:only-of-type Selects every <p> element that is the only <p> element of its parent
len2 = vParentVChildren.length;
count = 0;
for (k=0; (k<len2) && (count<=1); k++) {
(vParentVChildren[k].tag===vnode.tag) && count++;
}
if (count!==1) {
return false;
}
break;
case PSEUDO_ONLY_CHILD: // p:only-child Selects every <p> element that is the only child of its parent
if (vParentVChildren.length!==1) {
return false;
}
break;
case ':optional': // input:optional Selects input elements with no "required" attribute
if (vnode.attrs.required) {
return false;
}
break;
case ':out-of-range': // input:out-of-range Selects input elements with a value outside a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || ((value>=min) && (value<=max))) {
return false;
}
break;
case ':read-only': // input:read-only Selects input elements with the "readonly" attribute specified
if (!vnode.attrs.readonly) {
return false;
}
break;
case ':read-write': // input:read-write Selects input elements with the "readonly" attribute NOT specified
if (vnode.attrs.readonly) {
return false;
}
break;
case ':required': // input:required Selects input elements with the "required" attribute specified
if (!vnode.attrs.required) {
return false;
}
break;
case ':root': // Selects the document's root element
if (vnode.domNode!==DOCUMENT.documentElement) {
return false;
}
break;
}
}
}
return true;
};
/**
* Splits the selector into separate subselector-items that should match different elements through the tree.
* Special characters '>' and '+' are added as separate items in the hash.
*
* @method _splitSelector
* @param selector {String} the selector-item to check the match for
* @return {Array} splitted selectors
* @protected
* @private
* @since 0.0.1
*/
_splitSelector = function(selector) {
var list = [],
len = selector.length,
sel = '',
i, character, insideDataAttr;
for (i=0; i<len; i++) {
character = selector[i];
if (character==='[') {
sel += character;
insideDataAttr = true;
}
else if (character===']') {
sel += character;
insideDataAttr = false;
}
else if (insideDataAttr || !SPLIT_CHARACTER[character]) {
sel += character;
}
else {
// unique selectoritem is found, add it to the list
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
// in case the last character was '>', '+' or '~', we need to add it as a separate item
STORABLE_SPLIT_CHARACTER[character] && (list[list.length]=character);
}
}
// add the last item
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
return list;
};
_batchEmit = function() {
// we will exactly define the 'UI:'-event --> Itags will have another emitterName
MUTATION_EVENTS.each(function (mutationEvents, vnode) {
var domNode = vnode.domNode;
if (mutationEvents[EV_REMOVED]) {
domNode.emit('UI:'+EV_REMOVED);
}
else if (mutationEvents[EV_INSERTED]) {
domNode.emit('UI:'+EV_INSERTED);
}
else {
// contentchange and attributechanges can go hand in hand
mutationEvents.each(function(value, evt) {
domNode.emit('UI:'+evt, (evt===EV_CONTENT_CHANGE) ? null : {changed: value});
});
}
});
MUTATION_EVENTS.clear();
BATCH_WILL_RUN = false;
};
_emitDestroyChildren = function(vnode) {
var children = vnode.vChildren,
len = children.length,
i, vChild;
for (i=0; i<len; i++) {
vChild = children[i];
vChild._emit(EV_REMOVED);
}
};
_markRemoved = function(vnode) {
var vChildNodes = vnode.vChildNodes,
len, i, vChildNode;
if (vnode.nodeType===1) {
Object.protectedProp(vnode, 'removedFromDOM', true);
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && _markRemoved(vChildNode);
}
}
}
};
/**
* A safe way to remove a dom-node, even if it is not present.
* Will not throw a JS error when the "to be removed" node isn't in the dom
*
* @method _tryRemoveDomNode
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param childDomNode {DOMNode} the node that needs to be removed
* @since 0.0.1
*/
_tryRemoveDomNode = function(parentDomNode, childDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && childDomNode.isItag && childDomNode.isItag()) {
DOCUMENT._itagList.remove(childDomNode);
}
try {
parentDomNode._removeChild(childDomNode);
}
catch(err) {}
};
/**
* A safe way to replace a dom-node, even if the "to be replaced"-node it is not present.
* Will not throw a JS error when the "to be replaced" node isn't in the dom. in that case, the new node will
* be appended
*
* @method _tryReplaceChild
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param newChildDomNode {DOMNode} the node that needs to be replaced
* @param oldChildDomNode {DOMNode} the node that needs to be inserted
* @since 0.0.1
*/
_tryReplaceChild = function(parentDomNode, newChildDomNode, oldChildDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && oldChildDomNode.isItag && oldChildDomNode.isItag()) {
DOCUMENT._itagList.remove(oldChildDomNode);
}
try {
parentDomNode._replaceChild(newChildDomNode, oldChildDomNode);
}
catch(err) {
// if the childDomNode isn;t there any more - for whatever reason - the we need to append the newChildNode
parentDomNode._appendChild(newChildDomNode);
}
};
vNodeProto = window._ITSAmodules.VNode = {
/**
* Check whether the vnode's domNode is equal, or contains the specified Element.
*
* @method contains
* @return {Boolean} whether the vnode's domNode is equal, or contains the specified Element.
* @since 0.0.1
*/
contains: function(otherVNode, noProtectedSearch) {
var instance = this;
if (otherVNode && (otherVNode.destroyed || (noProtectedSearch && otherVNode._systemNode))) {
return false;
}
while (otherVNode && (otherVNode!==instance)) {
otherVNode = otherVNode.vParent;
if (otherVNode && noProtectedSearch && (otherVNode._systemNode || (otherVNode.isItag && otherVNode.domNode.contentHidden))) {
return false;
}
}
return (otherVNode===instance);
},
/**
* Empties the vnode.
*
* Syncs with the dom.
*
* @method empty
* @param [full=false] {Boolean} whether system-nodes should be removed as well
* @chainable
* @since 0.0.1
*/
empty: function(full) {
delete this._scripts;
return this._setChildNodes([], null, full);
},
/**
* Returns the first child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method firstOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the first child-vnode or null when not present
* @since 0.0.1
*/
firstOfVChildren: function(cssSelector) {
var instance = this,
found, i, len, vChildren, element;
if (!cssSelector) {
return instance.vFirstElementChild;
}
vChildren = instance.vChildren;
len = vChildren.length;
for (i=0; !found && (i<len); i++) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
return found;
},
/**
* Gets the innerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* Only valid for nodetype=1 (HTMLElements)
*
* @method getHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String|undefined} the innerHTML without the elements specified, or `undefined` when not an HTMLElement
* @since 0.0.1
*/
getHTML: function(exclude, includeSystemNodes) {
var instance = this,
html, vChildNodes, len, i, vChildNode;
if (instance.nodeType===1) {
if (exclude) {
Array.isArray(exclude) || (exclude=[exclude]);
}
html = '';
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
switch (vChildNode.nodeType) {
case 1:
(exclude && exclude.contains(vChildNode.domNode)) || (!includeSystemNodes && vChildNode._systemNode) || (html+=vChildNode.getOuterHTML(exclude, includeSystemNodes));
break;
case 3:
html += vChildNode.text.replace(/</g, '<').replace(/>/g, '>');
break;
case 8:
html += '<!--' + vChildNode.text.replace(/</g, '<').replace(/>/g, '>') + '-->';
}
}
}
return html;
},
/**
* Gets the outerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* @method getOuterHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String} the outerHTML
* @since 0.0.1
*/
getOuterHTML: function(exclude, includeSystemNodes) {
var instance = this,
html,
attrs = instance.attrs;
if (instance.nodeType===1) {
if (instance.nodeType!==1) {
return instance.textContent;
}
html = '<' + instance.tag.toLowerCase();
attrs.each(function(value, key) {
html += ' '+key+'="'+value+'"';
});
instance.isVoid && (html += '/');
html += '>';
if (!instance.isVoid) {
html += ((!includeSystemNodes && instance.isItag) ? '' : instance.getHTML(exclude, includeSystemNodes)) + '</' + instance.tag.toLowerCase() + '>';
}
}
return html;
},
/**
* Checks whether the vnode has any vChildNodes (nodeType of 1, 3 or 8).
*
* @method hasVChildNodes
* @return {Boolean} whether the vnode has any vChildNodes.
* @since 0.0.1
*/
hasVChildNodes: function() {
return this.vChildNodes ? (this.vChildNodes.length>0) : false;
},
/**
* Checks whether the vnode has any vChildren (vChildNodes with nodeType of 1).
*
* @method hasVChildren
* @return {Boolean} whether the vnode has any vChildren.
* @since 0.0.1
*/
hasVChildren: function() {
return this.vChildNodes ? (this.vChildren.length>0) : false;
},
/**
* Checks whether the className is present on the vnode.
*
* @method hasClass
* @param className {String|Array} the className to check for. May be an Array of classNames, which all needs to be present.
* @return {Boolean} whether the className (or classNames) is present on the vnode
* @since 0.0.1
*/
hasClass: function(className) {
var instance = this,
check = function(cl) {
return !!instance.classNames[cl];
};
if (!instance.classNames) {
return false;
}
if (typeof className === STRING) {
return check(className);
}
else if (Array.isArray(className)) {
return className.every(check);
}
return false;
},
/**
* Returns the last child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method lastOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the last child-vnode or null when not present
* @since 0.0.1
*/
lastOfVChildren: function(cssSelector) {
var vChildren = this.vChildren,
found, i, element;
if (vChildren) {
if (!cssSelector) {
return this.vLastElementChild;
}
for (i=vChildren.length-1; !found && (i>=0); i--) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
}
return found;
},
/**
* Checks whether the vnode matches one of the specified selectors. `selectors` can be one, or multiple css-selectors,
* separated by a `comma`. For example: "#myid li.red blue" is one selector, "div.red, div.blue, div.green" are three selectors.
*
* @method matchesSelector
* @param selectors {String} one or more css-selectors
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches one of the selectors
* @since 0.0.1
*/
matchesSelector: function(selectors, relatedVNode) {
var instance = this;
if (instance.nodeType!==1) {
return false;
}
selectors = selectors.split(',');
// we can use Array.some, because there won't be many separated selectoritems,
// so the final invocation won't be delayed much compared to looping
return selectors.some(function(selector) {
return _matchesOneSelector(instance, selector, relatedVNode);
});
},
/**
* Reloads the DOM-attribute into the vnode.
*
* @method matchesSelector
* @param attributeName {String} the name of the attribute to be reloaded.
* @return {Node} the domNode that was reloaded.
* @since 0.0.1
*/
reloadAttr: function(attributeName) {
var instance = this,
domNode = instance.domNode,
attributeValue = domNode._getAttribute(attributeName),
attrs = instance.attrs,
extractStyle, extractClass;
if (instance.nodeType===1) {
attributeValue || (attributeValue='');
if (attributeValue==='') {
delete attrs[attributeName];
// in case of STYLE attributeName --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attributeName --> special treatment
(attributeName===CLASS) && (instance.classNames={});
// in case of ID attributeName --> special treatment
if ((attributeName===ID) && (instance.id)) {
delete nodeids[instance.id];
delete instance.id;
}
}
else {
attrs[attributeName] = attributeValue;
// in case of STYLE attributeName --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(attributeValue);
attributeValue = extractStyle.attrStyle;
if (attributeValue) {
attrs.style = attributeValue;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attributeName --> special treatment
extractClass = extractor.extractClass(attributeValue);
attributeValue = extractClass.attrClass;
if (attributeValue) {
attrs[CLASS] = attributeValue;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (instance.id!==attributeValue) && (delete nodeids[instance.id]);
instance.id = attributeValue;
nodeids[attributeValue] = domNode;
}
}
}
return domNode;
},
/**
* Returns the vnode's style in a serialized form: the way it appears in the dom.
*
* @method serializeStyles
* @return {String} vnode's style
* @since 0.0.1
*/
serializeStyles: function() {
return extractor.serializeStyles(this.styles);
},
/**
* Sets the vnode's and dom-nodes inner HTML.
*
* Syncs with the dom. Can be invoked multiple times without issues.
*
* @method setHTML
* @param content {String} the innerHTML
* @param [suppressItagRender] {Boolean} to suppress Itags from rendering
* @param [allowScripts=false] {Boolean} whether scripts are allowed --> these should be defined with `xscript` instead of `script`
* @chainable
* @since 0.0.1
*/
setHTML: function(content, suppressItagRender, allowScripts) {
var instance = this;
instance._setChildNodes(htmlToVNodes(content, vNodeProto, instance.ns, null, suppressItagRender, allowScripts), suppressItagRender);
return instance;
},
/**
* Syncs the vnode's nodeid (if available) inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom. Can be invoked multiple times without issues.
*
* @method storeId
* @chainable
* @since 0.0.1
*/
storeId: function() {
// store node/vnode inside WeakMap:
var instance = this;
instance.id ? (nodeids[instance.id]=instance.domNode) : (delete nodeids[instance.id]);
return instance;
},
//---- private ------------------------------------------------------------------
_addToTaglist: function() {
var instance = this,
itagList;
if (instance.isItag) {
itagList = DOCUMENT.getItags(); // also reads the dom if the list isn't build yet
instance._data || Object.protectedProp(instance, '_data', {});
if (!instance._data.ce_destroyed && !itagList.contains(instance.domNode)) {
itagList.push(instance.domNode);
}
}
},
/**
* Adds a vnode to the end of the list of vChildNodes.
*
* Syncs with the DOM.
*
* @method _appendChild
* @param VNode {vnode} vnode to append
* @private
* @return {Node} the Node that was appended
* @since 0.0.1
*/
_appendChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
size, noProccessScript, scriptContent;
if ((VNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (VNode.attrs && VNode.attrs.src) ? VNode.attrs.src : VNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
VNode._moveToParent(instance);
instance.domNode._appendChild(domNode);
if (VNode.nodeType===3) {
size = instance.vChildNodes.length;
instance._normalize();
// if the size changed, then the domNode was merged
(size===instance.vChildNodes.length) || (domNode=instance.vChildNodes[instance.vChildNodes.length-1].domNode);
}
if (VNode.nodeType===1) {
VNode._addToTaglist();
VNode._emit(EV_INSERTED);
}
return domNode;
}
},
/**
* Removes the vnode from its parent vChildNodes- and vChildren-list.
*
* Does NOT sync with the dom.
*
* @method _deleteFromParent
* @private
* @chainable
* @since 0.0.1
*/
_deleteFromParent: function() {
var instance = this,
vParent = instance.vParent;
if (vParent && vParent.vChildNodes) {
vParent.vChildNodes.remove(instance);
// force to recalculate the vChildren on a next call:
(instance.nodeType===1) && (vParent._vChildren=null);
}
return instance;
},
/**
* Cleans up (empties) vnode's `_data`
*
* @method _cleanData
* @private
* @chainable
* @since 0.0.1
*/
_cleanData: function() {
var instance = this,
data = instance._data;
data && data.each(
function(value, key) {
delete data[key];
}
);
return instance;
},
/**
* Cleans up (removes) duplicated `style` definitions.
*
* @method _cleanupStyle
* @private
* @chainable
* @since 0.0.1
*/
_cleanupStyle: function() {
var instance = this,
compare = [],
removal = [],
vChildren = instance.vChildren,
len = vChildren.length,
vChild, i, styleContent, vChildInner;
for (i=0; i<len; i++) {
vChild = vChildren[i];
if (vChild.tag==='STYLE') {
vChildInner = vChild.vChildNodes[0];
if (vChildInner) {
styleContent = vChildInner.text;
if (compare.contains(styleContent)) {
removal[removal.length] = vChild;
}
else {
compare[compare.length] = styleContent;
}
}
else {
// empty style-tag
removal[removal.length] = vChild;
}
}
}
removal.forEach(function(vnode) {
instance._removeChild(vnode);
});
},
/**
* Destroys the vnode and all its vnode-vChildNodes.
* Removes it from its vParent.vChildNodes list,
* also removes its definitions inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom.
*
* @method _destroy
* @private
* @chainable
* @since 0.0.1
*/
_destroy: function(silent) {
var instance = this,
vChildNodes = instance.vChildNodes,
len, i, vChildNode, vParent, treeNodes;
if (!instance.destroyed) {
if (!silent) {
// Because we don't want to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
later(function() {
instance._emit(EV_REMOVED, null, null, null, true);
}, 5);
}
Object.protectedProp(instance, 'destroyed', true);
// first: determine the dom-tree, which module `event-dom` needs to determine where the node was before it was destroyed:
treeNodes = [instance];
vParent = instance.vParent;
while (vParent) {
treeNodes[treeNodes.length] = vParent;
vParent = vParent.vParent;
}
// mark all its vChildNodes so we can see if the node is in the DOM
_markRemoved(instance);
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && instance.isItag) {
DOCUMENT._itagList.remove(instance.domNode);
}
instance._scripts && (instance._scripts.length=0);
// The definite cleanup needs to be done after a timeout:
// someone might need to handle the Element when removed (fe to cleanup specific things)
later(function() {
instance._cleanData();
if (instance.nodeType===1) {
// _destroy all its vChildNodes
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && vChildNode._destroy(true);
}
}
}
instance._vChildren = null;
// explicitely set instance.domNode._vnode and instance.domNode to null in order to prevent problems with the GC (we break the circular reference)
delete instance.domNode._vnode;
// if valid id, then _remove the DOMnodeRef from internal hash
instance.id && delete nodeids[instance.id];
}, silent ? 0 : DESTROY_DELAY);
instance._deleteFromParent();
// Do not make domNode `null` --> it could be used even when not in the dom
}
return instance;
},
_emit: function(evt, attribute, newValue, prevValue, destroyEvt) {
/**
* Emitted by every Element that gets inserted.
*
* @event nodeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets removed.
*
* @event noderemove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets its content changed (innerHTML/innerText).
*
* @event nodecontentchange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute inserted.
*
* @event attributeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* </ul>
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute removed.
*
* @event attributeremove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Strings of the attributeNames that are removed
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute changed.
*
* @event attributechange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* <li>prevValue</li>
* </ul>
* @since 0.1
*/
var instance = this,
silent, attrMutations, mutationEvents, mutation, vParent;
if (!DOCUMENT.hasMutationSubs || (instance.nodeType!==1)) {
return;
}
silent = !!DOCUMENT._suppressMutationEvents;
if (!silent && (destroyEvt || !instance.destroyed)) {
// Because we don't wannt to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
mutationEvents = MUTATION_EVENTS.get(instance) || {};
if (attribute) {
attrMutations = mutationEvents[evt] || [];
if (evt===EV_ATTRIBUTE_REMOVED) {
mutation = attribute;
}
else {
mutation = {
attribute: attribute
};
if ((evt===EV_ATTRIBUTE_INSERTED) || (evt===EV_ATTRIBUTE_CHANGED)) {
mutation.newValue = newValue;
}
if ((evt===EV_ATTRIBUTE_CHANGED) && prevValue) {
mutation.prevValue = prevValue;
}
}
attrMutations.push(mutation);
mutationEvents[evt] = attrMutations;
}
else {
mutationEvents[evt] = true;
}
MUTATION_EVENTS.set(instance, mutationEvents);
// now set all parent to have a nodecontentchange:
vParent = instance.vParent;
vParent && vParent._emit(EV_CONTENT_CHANGE);
// in case of removal we need to emit EV_REMOVED for all children right now
// for they will be actually removed silently after a delay of 1 minute
(evt===EV_REMOVED) && _emitDestroyChildren(instance);
if (!BATCH_WILL_RUN) {
BATCH_WILL_RUN = true;
async(function() {
_batchEmit();
});
}
}
return instance;
},
/**
* Inserts `newVNode` before `refVNode`.
*
* Syncs with the DOM.
*
* @method _insertBefore
* @param newVNode {vnode} vnode to insert
* @param refVNode {vnode} The vnode before which newVNode should be inserted.
* @private
* @return {Node} the Node being inserted (equals domNode)
* @since 0.0.1
*/
_insertBefore: function(newVNode, refVNode) {
var instance = this,
domNode = newVNode.domNode,
index = instance.vChildNodes.indexOf(refVNode),
noProccessScript, scriptContent;
if ((newVNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (newVNode.attrs && newVNode.attrs.src) ? newVNode.attrs.src : newVNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
if (index!==-1) {
newVNode._moveToParent(instance, index);
instance.domNode._insertBefore(domNode, refVNode.domNode);
(newVNode.nodeType===3) && instance._normalize();
if (newVNode.nodeType===1) {
newVNode._addToTaglist();
newVNode._emit(EV_INSERTED);
}
return domNode;
}
else {
console.warn('trying to insert before, but no ref-node found. Will append the new node');
return instance._appendChild(newVNode);
}
}
},
/**
* Moves the vnode from its current parent.vChildNodes list towards a new parent vnode at the specified position.
*
* Does NOT sync with the dom.
*
* @method _moveToParent
* @param parentVNode {vnode} the parent-vnode
* @param [index] {Number} the position of the child. When not specified, it will be appended.
* @private
* @chainable
* @since 0.0.1
*/
_moveToParent: function(parentVNode, index) {
var instance = this,
vParent = instance.vParent;
instance._deleteFromParent();
instance.vParent = parentVNode;
parentVNode.vChildNodes || (parentVNode.vChildNodes=[]);
(typeof index==='number') ? parentVNode.vChildNodes.insertAt(instance, index) : (parentVNode.vChildNodes[parentVNode.vChildNodes.length]=instance);
// force to recalculate the vChildren on a next call:
vParent && (instance.nodeType===1) && (vParent._vChildren = null);
// force to recalculate the vChildren on a next call:
parentVNode && (instance.nodeType===1) && (parentVNode._vChildren=null);
return instance;
},
/**
* Removes empty TextNodes and merges following TextNodes inside the vnode.
*
* Syncs with the dom.
*
* @method _normalize
* @private
* @chainable
* @since 0.0.1
*/
_normalize: function() {
var instance = this,
domNode = instance.domNode,
vChildNodes = instance.vChildNodes,
changed = false,
i, preChildNode, vChildNode;
if (!instance._unNormalizable && vChildNodes) {
for (i=vChildNodes.length-1; i>=0; i--) {
vChildNode = vChildNodes[i];
preChildNode = vChildNodes[i-1]; // i will get the value `-1` eventually, which leads into undefined preChildNode
if (vChildNode.nodeType===3) {
if (vChildNode.text==='') {
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
else if (preChildNode && preChildNode.nodeType===3) {
preChildNode.text += vChildNode.text;
preChildNode.domNode.nodeValue = unescapeEntities(preChildNode.text);
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
}
}
}
changed && instance._emit(EV_CONTENT_CHANGE);
return instance;
},
/**
* Makes the vnode `normalizable`. Could be set to `false` when batch-inserting nodes, while `normalizaing` manually at the end.
* Afterwards, you should always reset `normalizable` to true.
*
* @method _normalizable
* @param value {Boolean} whether the vnode should be normalisable.
* @private
* @chainable
* @since 0.0.1
*/
_normalizable: function(value) {
var instance = this;
value ? (delete instance._unNormalizable) : (instance._unNormalizable=true);
return instance;
},
/**
* Prevents MutationObserver from making the dom sync with the vnode.
* Should be used when manipulating the dom from within the vnode itself (to preventing looping)
*
* @method _noSync
* @chainable
* @private
* @since 0.0.1
*/
_noSync: function() {
var instance = this;
if (!instance._nosync) {
instance._nosync = true;
async(function() {
instance._nosync = false;
});
}
return instance;
},
/**
* Removes the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _removeAttr
* @param attributeName {String}
* @private
* @chainable
* @since 0.0.1
*/
_removeAttr: function(attributeName, suppressItagRender) {
var instance = this,
attributeNameSplitted, ns;
if (instance.isItag && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
console.warn('Not allowed to remove the attribute '+attributeName);
return instance;
}
if (instance.attrs[attributeName]!==undefined) {
delete instance.attrs[attributeName];
// in case of STYLE attribute --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attribute --> special treatment
(attributeName===CLASS) && (instance.classNames={});
if (attributeName===ID) {
delete nodeids[instance.id];
delete instance.id;
}
instance._emit(EV_ATTRIBUTE_REMOVED, attributeName);
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
instance.domNode._removeAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName);
}
else {
instance.domNode._removeAttribute(attributeName);
}
}
return instance;
},
/**
* Removes the vnode's child-vnode from its vChildren and the DOM.
*
* Syncs with the DOM.
*
* @method removeChild
* @param VNode {vnode} the child-vnode to remove
* @private
* @since 0.0.1
*/
_removeChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
hadFocus = domNode && domNode.hasFocus && domNode.hasFocus() && (VNode.attrs['fm-lastitem']==='true'),
parentVNode = VNode.vParent;
VNode._destroy();
_tryRemoveDomNode(instance.domNode, VNode.domNode);
instance._normalize();
// now, reset the focus on focusmanager when needed:
if (hadFocus) {
while (parentVNode && !parentVNode.attrs['fm-manage']) {
parentVNode = parentVNode.vParent;
}
parentVNode && parentVNode.domNode.focus();
}
},
/**
* Replaces the current vnode at the parent.vChildNode list by `newVNode`
*
* Does NOT sync with the dom.
*
* @method _replaceAtParent
* @param newVNode {Object} the new vnode which should take over the place of the current vnode
* @private
* @chainable
* @since 0.0.1
*/
_replaceAtParent: function(newVNode) {
var instance = this,
vParent = instance.vParent,
vChildNodes, index;
if (vParent && (vChildNodes=vParent.vChildNodes)) {
index = vChildNodes.indexOf(instance);
// force to recalculate the vChildren on a next call:
((instance.nodeType===1) || (newVNode.nodeType===1)) && (instance.vParent._vChildren=null);
vChildNodes[index] = newVNode;
}
return instance._destroy();
},
/**
* Sets the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _setAttr
* @param attributeName {String}
* @param value {String} the value for the attributeName
* @param [force=false] {Boolean} force the attribute to be set, even if restrictions would deny it
* @private
* @chainable
* @since 0.0.1
*/
_setAttr: function(attributeName, value, force, suppressItagRender) {
var instance = this,
extractStyle, extractClass,
attrs = instance.attrs,
prevVal = attrs[attributeName],
domNode = instance.domNode,
attributeNameSplitted, ns;
if (instance.isItag && !force && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
if (prevVal!=value) {
console.warn('Not allowed to set the attribute '+attributeName);
}
return instance;
}
// don't check by !== --> value isn't parsed into a String yet
if (prevVal && ((value===undefined) || (value===null))) {
instance._removeAttr(attributeName, suppressItagRender);
return instance;
}
// attribute-values are always Strings:
value = String(value);
// attribute-values will be stored without " or '
value = value.replace(/"/g, '"').replace(/'/g, "'");
if (prevVal!=value) {
attrs[attributeName] = value;
// in case of STYLE attribute --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(value);
value = extractStyle.attrStyle;
if (value) {
attrs.style = value;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attribute --> special treatment
extractClass = extractor.extractClass(value);
value = extractClass.attrClass;
if (value) {
attrs[CLASS] = value;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (delete nodeids[instance.id]);
instance.id = value;
nodeids[value] = domNode;
}
instance._emit(prevVal ? EV_ATTRIBUTE_CHANGED : EV_ATTRIBUTE_INSERTED, attributeName, value, prevVal);
// when set in the dom --> quotes need to be set as "
value = value.replace(/"/g, '"');
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
domNode._setAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName, value);
}
else {
domNode._setAttribute(attributeName, value);
}
}
return instance;
},
/**
* Redefines the attributes of both the vnode as well as its related dom-node. The new
* definition replaces any previous attributes (without touching unmodified attributes).
* the `is` attribute cannot be changed for itags.
*
* Syncs the new vnode's attributes with the dom.
*
* @method _setAttrs
* @param newAttrs {Object|Array} the new attributes to be set
* @private
* @chainable
* @since 0.0.1
*/
_setAttrs: function(newAttrs, suppressItagRender) {
// does sync the DOM
var instance = this,
attrsObj, attr, attrs, i, key, keys, len, value;
if (instance.nodeType!==1) {
return;
}
instance._noSync();
attrs = instance.attrs;
attrs.id && (delete nodeids[attrs.id]);
if (Object.isObject(newAttrs)) {
attrsObj = newAttrs;
}
else {
attrsObj = {};
len = newAttrs.length;
for (i=0; i<len; i++) {
attr = newAttrs[i];
attrsObj[attr.name] = attr.value;
}
}
if (instance.isItag && !suppressItagRender) {
if (attrs.is) {
attrsObj.is = attrs.is;
}
else {
delete attrsObj.is;
delete attrsObj.Is;
delete attrsObj.iS;
delete attrsObj.IS;
}
}
// first _remove the attributes that are no longer needed.
// quickest way for object iteration: http://jsperf.com/object-keys-iteration/20
keys = Object.keys(attrs);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
attrsObj[key] || instance._removeAttr(key, suppressItagRender);
}
// next: every attribute that differs: redefine
keys = Object.keys(attrsObj);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
value = attrsObj[key];
(attrs[key]===value) || instance._setAttr(key, value, suppressItagRender);
}
return instance;
},
/**
* Redefines the childNodes of both the vnode as well as its related dom-node. The new
* definition replaces any previous nodes. (without touching unmodified nodes).
*
* Syncs the new vnode's childNodes with the dom.
*
* @method _setChildNodes
* @param newVChildNodes {Array} array with vnodes which represent the new childNodes
* @private
* @chainable
* @since 0.0.1
*/
_setChildNodes: function(newVChildNodes, suppressItagRender, removeSystemElements) {
// does sync the DOM
var instance = this,
vChildNodes = instance.vChildNodes || [],
domNode = instance.domNode,
forRemoval = [],
i, oldChild, newChild, newLength, len, len2, childDomNode, nodeswitch, bkpAttrs, cleanupStyle,
process, bkpChildNodes, needNormalize, prevSuppress, scriptContent, _scripts, scriptLen, j;
instance._noSync();
// first: reset ._vChildren --> by making it empty, its getter will refresh its list on a next call
instance._vChildren = null;
// if newVChildNodes is undefined, then we assume it to be empty --> an empty array
newVChildNodes || (newVChildNodes=[]);
// quickest way to loop through array is by using for loops: http://jsperf.com/array-foreach-vs-for-loop/5
len = vChildNodes.length;
// we need to add the systemNodes -if any- to the new newVChildNodes: they should be retained.
// SystemNodes are always places at the beginning, so they won't get reshuffled:
if (!removeSystemElements) {
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
if (oldChild._systemNode) {
newVChildNodes.insertAt(oldChild, i);
}
else {
break;
}
}
}
newLength = newVChildNodes.length;
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
childDomNode = oldChild.domNode;
if (i < newLength) {
newChild = newVChildNodes[i];
newChild.vParent || (newChild.vParent=instance);
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
/*jshint boss:true */
switch (nodeswitch=NODESWITCH[oldChild.nodeType][newChild.nodeType]) {
/*jshint boss:false */
case 1: // oldNodeType==Element, newNodeType==Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
if ((oldChild.tag!==newChild.tag) ||
((oldChild.tag===newChild.tag) && oldChild.isItag && (oldChild.attrs.is!==newChild.attrs.is))) {
// new tag --> completely replace
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
/*jshint proto:true */
oldChild.isItag && oldChild.domNode.destroyUI && oldChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
newChild.attrs = {}; // reset to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
oldChild._replaceAtParent(newChild);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
else {
// same tag --> only update what is needed
// NOTE: when this._unchangableAttrs exists, an itag-element syncs its UI -->
if (oldChild._data) {
// we might need to set the class `itag-rendered` when the attributeData says so:
// this happens when an itag gets refreshed with an unrendered definition
if (oldChild._data.itagRendered && !newChild.hasClass('itag-rendered')) {
newChild.classNames['itag-rendered'] = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' '+'itag-rendered';
}
else {
newChild.attrs[CLASS] = 'itag-rendered';
}
}
// we might need to set the class `focussed` when the attributeData says so:
// this happens when an itag gets rerendered: its renderFn doesn't know if any elements
// were focussed
if (oldChild._data.focussed && !newChild.hasClass('focussed')) {
newChild.classNames.focussed = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' ' + 'focussed';
}
else {
newChild.attrs[CLASS] = 'focussed';
}
}
if (oldChild._data['fm-tabindex']) {
// node has the tabindex set by the focusmanager,
// but that info might got lost with re-rendering of the new element
newChild.attrs.tabindex = '0';
}
}
if (oldChild.isItag) {
prevSuppress = DOCUMENT._suppressMutationEvents || false;
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
/*jshint proto:true */
newChild.domNode.destroyUI && newChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
oldChild._setAttrs(newChild.attrs, suppressItagRender);
newChild._destroy(true); // destroy through the vnode and removing from DOCUMENT._itagList
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(prevSuppress);
}
else {
oldChild._setAttrs(newChild.attrs, suppressItagRender);
// next: sync the vChildNodes:
oldChild._setChildNodes(newChild.vChildNodes, suppressItagRender);
}
// reset ref. to the domNode, for it might have been changed by newChild:
oldChild.id && (nodeids[oldChild.id]=childDomNode);
newVChildNodes[i] = oldChild;
}
}
break;
case 2: // oldNodeType==Element, newNodeType==TextNode
// case2 and case3 should be treated the same
case 3: // oldNodeType==Element, newNodeType==Comment
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
oldChild._replaceAtParent(newChild);
instance._emit(EV_CONTENT_CHANGE);
break;
case 4: // oldNodeType==TextNode, newNodeType==Element
// case4 and case7 should be treated the same
case 7: // oldNodeType==Comment, newNodeType==Element
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
// oldChild.isVoid = newChild.isVoid;
// delete oldChild.text;
instance._emit(EV_CONTENT_CHANGE);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
break;
case 5: // oldNodeType==TextNode, newNodeType==TextNode
// case5 and case9 should be treated the same
case 9: // oldNodeType==Comment, newNodeType==Comment
if (oldChild.text!==newChild.text) {
oldChild.text = newChild.text;
oldChild.domNode.nodeValue = unescapeEntities(newChild.text);
instance._emit(EV_CONTENT_CHANGE);
}
newVChildNodes[i] = oldChild;
break;
case 6: // oldNodeType==TextNode, newNodeType==Comment
// case6 and case8 should be treated the same
case 8: // oldNodeType==Comment, newNodeType==TextNode
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = oldChild.vParent;
instance._emit(EV_CONTENT_CHANGE);
}
if ((nodeswitch===2) || (nodeswitch===5) || (nodeswitch===8)) {
needNormalize = true;
}
}
else {
// _remove previous definition
_tryRemoveDomNode(domNode, oldChild.domNode);
// the oldChild needs to be removed, however, this cannot be done right now, for it would effect the loop
// so we store it inside a hash to remove it later
forRemoval[forRemoval.length] = oldChild;
}
}
// now definitely remove marked childNodes:
len2 = forRemoval.length;
for (i=0; i<len2; i++) {
forRemoval[i]._destroy();
}
// now we add all new vChildNodes that go beyond `len`:
for (i = len; i < newLength; i++) {
newChild = newVChildNodes[i];
newChild.vParent = instance;
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
switch (newChild.nodeType) {
case 1: // Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
domNode._appendChild(newChild.domNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
break;
case 3: // TextNode
needNormalize = true;
// we need to break through --> no `break`
/* falls through */
default: // TextNode or CommentNode
// newChild.domNode.nodeValue = newChild.text;
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
domNode._appendChild(newChild.domNode);
instance._emit(EV_CONTENT_CHANGE);
}
newChild.storeId();
}
instance.vChildNodes = newVChildNodes;
needNormalize && instance._normalize();
cleanupStyle && instance._cleanupStyle();
return instance;
},
_setUnchangableAttrs: function(unchangableObj) {
this._unchangableAttrs = unchangableObj;
}
};
//---- properties ------------------------------------------------------------------
/**
* A hash of all the `attributes` of the vnode's representing dom-node.
*
* @property attrs
* @type Object
* @since 0.0.1
*/
/**
* Hash with all the classes of the vnode. Every class represents a key, all values are set `true`.
*
* @property classNames
* @type Object
* @since 0.0.1
*/
/**
* The `id` of the vnode's representing dom-node (if any).
*
* @property id
* @type String
* @since 0.0.1
*/
/**
* Tells whether tag is a void Element. Examples are: `br`, `img` and `input`. Non-void Elements are f.e. `div` and `table`.
* For TextNodes and CommentNodes, this property is `undefined`.
*
* @property isVoid
* @type Boolean
* @since 0.0.1
*/
/**
* The `nodeType` of the vnode's representing dom-node (1===ElementNode, 3===TextNode, 8===CommentNode).
*
* @property nodeType
* @type Number
* @since 0.0.1
*/
/**
* The `tag` of the vnode's representing dom-node (allways uppercase).
*
* @property tag
* @type String
* @since 0.0.1
*/
/**
* The `content` of the vnode's representing dom-node, in case it is a TextNode or CommentNode.
* Equals dom-node.nodeValue.
*
* Is `undefined` for ElementNodes.
*
* @property text
* @type String
* @since 0.0.1
*/
/**
* Hash with all the childNodes (vnodes). vChildNodes are any kind of vnodes (nodeType===1, 3 or 8)
*
* @property vChildNodes
* @type Array
* @since 0.0.1
*/
/**
* The underlying `dom-node` that the vnode represents.
*
* @property domNode
* @type domNode
* @since 0.0.1
*/
/**
* vnode's parentNode (defined as a vnode itself).
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
Object.defineProperties(vNodeProto, {
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property innerHTML
* @type String
* @since 0.0.1
*/
innerHTML: {
get: function() {
return this.getHTML();
},
set: function(v) {
this.setHTML(v);
}
},
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property nodeValue
* @type String
* @since 0.0.1
*/
nodeValue: {
get: function() {
var instance = this;
return ((instance.nodeType===3) || (instance.nodeType===8)) ? instance.text : null;
},
set: function(v) {
var instance = this,
newTextContent, prevTextContent;
if ((instance.nodeType===3) || (instance.nodeType===8)) {
prevTextContent = instance.domNode.textContent;
instance.domNode.textContent = v;
// set .text AFTER the dom-node is updated --> the content might be escaped!
newTextContent = instance.text = instance.domNode.textContent;
(newTextContent!==prevTextContent) && instance._emit(EV_CONTENT_CHANGE);
}
}
},
/**
* Gets or sets the outerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property outerHTML
* @type String
* @since 0.0.1
*/
outerHTML: {
get: function() {
return this.getOuterHTML();
},
set: function(v) {
var instance = this,
vParent = instance.vParent,
id = instance.attrs.id,
vnode, vnodes, bkpAttrs, bkpChildNodes, i, len, vChildNodes, isLastChildNode, index, refDomNode;
if ((instance.nodeType!==1) || !vParent) {
return;
}
instance._noSync();
vChildNodes = vParent.vChildNodes;
index = vChildNodes.indexOf(instance);
isLastChildNode = (index===(vChildNodes.length-1));
isLastChildNode || (refDomNode=vChildNodes[index+1].domNode);
vnodes = htmlToVNodes(v, vNodeProto, vParent.ns, vParent);
len = vnodes.length;
if (len>0) {
// the first vnode will replace the current instance:
vnode = vnodes[0];
if (vnode.nodeType===1) {
if (vnode.tag!==instance.tag) {
// new tag --> completely replace
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
id && (delete nodeids[id]);
vnode.attrs = {}; // reset to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
// vnode.attrs = bkpAttrs;
// vnode.vChildNodes = bkpChildNodes;
vnode.id && (nodeids[vnode.id]=vnode.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
else {
instance._setAttrs(vnode.attrs);
instance._setChildNodes(vnode.vChildNodes);
}
}
else {
id && (delete nodeids[id]);
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
}
for (i=1; i<len; i++) {
vnode = vnodes[i];
switch (vnode.nodeType) {
case 1: // Element
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
vnode.attrs = {}; // reset, to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._insertBefore(vnode.domNode, refDomNode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
break;
default: // TextNode or CommentNode
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._appendChild(vnode.domNode, refDomNode);
}
vnode.storeId();
vnode._moveToParent(vParent, index+i);
}
}
},
/**
* Gets or sets the innerContent of the Node as plain text.
*
* The setter syncs with the DOM.
*
* @property textContent
* @type String
* @since 0.0.1
*/
textContent: {
get: function() {
var instance = this,
text = '',
vChildNodes = instance.vChildNodes,
len, i, vChildNode;
if (instance.nodeType===1) {
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
text += (vChildNode.nodeType===3) ? vChildNode.text : ((vChildNode.nodeType===1) ? vChildNode.textContent : '');
}
}
else {
text = instance.text;
}
return text;
},
set: function(v) {
var vnode = Object.create(vNodeProto);
vnode.domNode = DOCUMENT.createTextNode(v);
// create circular reference:
vnode.domNode._vnode = vnode;
vnode.nodeType = 3;
vnode.text = vnode.domNode.textContent;
this._setChildNodes([vnode]);
}
},
/**
* Hash with all the children (vnodes). vChildren are vnodes that have a representing dom-node that is an HtmlElement (nodeType===1)
*
* @property vChildren
* @type Array
* @since 0.0.1
*/
vChildren: {
get: function() {
var instance = this,
children = instance._vChildren,
vChildNode, vChildNodes, i, len;
vChildNodes = instance.vChildNodes;
if (vChildNodes && !children) {
children = instance._vChildren = [];
len = vChildNodes.length;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
(vChildNode.nodeType===1) && (children[children.length]=vChildNode);
}
}
children || (children = instance._vChildren = []);
return children;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirst
* @type vnode
* @since 0.0.1
*/
vFirst: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstChild;
}
},
/**
* Reference to the first vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirstChild
* @type vnode
* @since 0.0.1
*/
vFirstChild: {
get: function() {
return (this.vChildNodes && this.vChildNodes[0]) || null;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vFirstElement
* @type vnode
* @since 0.0.1
*/
vFirstElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstElementChild;
}
},
/**
* Reference to the first vChild, where the related dom-node an Element (nodeType===1).
*
* @property vFirstElementChild
* @type vnode
* @since 0.0.1
*/
vFirstElementChild: {
get: function() {
return this.vChildren[0] || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLast
* @type vnode
* @since 0.0.1
*/
vLast: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastChild;
}
},
/**
* Reference to the last vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLastChild
* @type vnode
* @since 0.0.1
*/
vLastChild: {
get: function() {
var vChildNodes = this.vChildNodes;
return (vChildNodes && vChildNodes[vChildNodes.length-1]) || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vLastElement
* @type vnode
* @since 0.0.1
*/
vLastElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastElementChild;
}
},
/**
* Reference to the last vChild, where the related dom-node an Element (nodeType===1).
*
* @property vLastElementChild
* @type vnode
* @since 0.0.1
*/
vLastElementChild: {
get: function() {
var vChildren = this.vChildren;
return vChildren[vChildren.length-1] || null;
}
},
/**
* the Parent vnode
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
/**
* Reference to the next of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vNext
* @type vnode
* @since 0.0.1
*/
vNext: {
get: function() {
return _findNodeSibling(this, true);
}
},
/**
* Reference to the next of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vNextElement
* @type vnode
* @since 0.0.1
*/
vNextElement: {
get: function() {
return _findElementSibling(this, true);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vPrevious
* @type vnode
* @since 0.0.1
*/
vPrevious: {
get: function() {
return _findNodeSibling(this);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vPreviousElement
* @type vnode
* @since 0.0.1
*/
vPreviousElement: {
get: function() {
return _findElementSibling(this);
}
}
});
return vNodeProto;
};
},{"./attribute-extractor.js":3392,"./html-parser.js":3396,"./vdom-ns.js":3398,"js-ext/extra/hashmap.js":3350,"js-ext/extra/lightmap.js":3351,"js-ext/lib/array.js":3352,"js-ext/lib/object.js":3353,"js-ext/lib/string.js":3355,"polyfill":3372,"utils/lib/timers.js":3375}],3400:[function(require,module,exports){
arguments[4][100][0].apply(exports,arguments)
},{"./partials/extend-document.js":3394,"./partials/extend-element.js":3395,"./partials/node-parser.js":3397,"js-ext/extra/hashmap.js":3350,"js-ext/lib/object.js":3353,"utils/lib/timers.js":3375}],3401:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":3403,"js-ext/extra/hashmap.js":3402,"polyfill/polyfill-base.js":3408}],3402:[function(require,module,exports){
module.exports=require(4)
},{}],3403:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3402,"polyfill/polyfill-base.js":3408,"utils":3409}],3404:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":3408}],3405:[function(require,module,exports){
module.exports=require(29)
},{}],3406:[function(require,module,exports){
module.exports=require(14)
},{}],3407:[function(require,module,exports){
module.exports=require(15)
},{}],3408:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3406,"./lib/window.console.js":3407}],3409:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3410,"./lib/timers.js":3411}],3410:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3402,"polyfill/polyfill-base.js":3414}],3411:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3414}],3412:[function(require,module,exports){
module.exports=require(14)
},{}],3413:[function(require,module,exports){
module.exports=require(15)
},{}],3414:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3412,"./lib/window.console.js":3413}],3415:[function(require,module,exports){
module.exports=require(14)
},{}],3416:[function(require,module,exports){
module.exports=require(15)
},{}],3417:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3415,"./lib/window.console.js":3416}],3418:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3421}],3419:[function(require,module,exports){
module.exports=require(14)
},{}],3420:[function(require,module,exports){
module.exports=require(15)
},{}],3421:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3419,"./lib/window.console.js":3420}],3422:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],3423:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":3427,"js-ext/extra/hashmap.js":3424,"polyfill/polyfill-base.js":3433}],3424:[function(require,module,exports){
module.exports=require(4)
},{}],3425:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":3426,"../lib/object.js":3427,"./classes.js":3423,"js-ext/extra/hashmap.js":3424,"polyfill/lib/weakmap.js":3431}],3426:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":3433}],3427:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3424,"polyfill/polyfill-base.js":3433,"utils":3434}],3428:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":3433}],3429:[function(require,module,exports){
module.exports=require(29)
},{}],3430:[function(require,module,exports){
module.exports=require(14)
},{}],3431:[function(require,module,exports){
module.exports=require(57)
},{}],3432:[function(require,module,exports){
module.exports=require(15)
},{}],3433:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3430,"./lib/window.console.js":3432}],3434:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3435,"./lib/timers.js":3436}],3435:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3424,"polyfill/polyfill-base.js":3439}],3436:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3439}],3437:[function(require,module,exports){
module.exports=require(14)
},{}],3438:[function(require,module,exports){
module.exports=require(15)
},{}],3439:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3437,"./lib/window.console.js":3438}],3440:[function(require,module,exports){
module.exports=require(66)
},{}],3441:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":3440}],3442:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":3440}],3443:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":3440}],3444:[function(require,module,exports){
module.exports=require(14)
},{}],3445:[function(require,module,exports){
module.exports=require(15)
},{}],3446:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3444,"./lib/window.console.js":3445}],3447:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3448,"./lib/timers.js":3449}],3448:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3424,"polyfill/polyfill-base.js":3452}],3449:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3452}],3450:[function(require,module,exports){
module.exports=require(14)
},{}],3451:[function(require,module,exports){
module.exports=require(15)
},{}],3452:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3450,"./lib/window.console.js":3451}],3453:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":3454}],3454:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":3455,"js-ext/lib/object.js":3456}],3455:[function(require,module,exports){
module.exports=require(4)
},{}],3456:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3455,"polyfill/polyfill-base.js":3459,"utils":3460}],3457:[function(require,module,exports){
module.exports=require(14)
},{}],3458:[function(require,module,exports){
module.exports=require(15)
},{}],3459:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3457,"./lib/window.console.js":3458}],3460:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3461,"./lib/timers.js":3462}],3461:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3455,"polyfill/polyfill-base.js":3465}],3462:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3465}],3463:[function(require,module,exports){
module.exports=require(14)
},{}],3464:[function(require,module,exports){
module.exports=require(15)
},{}],3465:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3463,"./lib/window.console.js":3464}],3466:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":3424,"js-ext/lib/object.js":3427,"js-ext/lib/string.js":3429,"polyfill":3446,"polyfill/extra/transition.js":3441,"polyfill/extra/vendorCSS.js":3443}],3467:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":3424,"js-ext/lib/object.js":3427,"polyfill":3446}],3468:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":3424,"js-ext/lib/object.js":3427,"js-ext/lib/string.js":3429,"polyfill":3446}],3469:[function(require,module,exports){
arguments[4][95][0].apply(exports,arguments)
},{"../css/element.css":3422,"./attribute-extractor.js":3466,"./element-array.js":3467,"./html-parser.js":3470,"./node-parser.js":3471,"./vdom-ns.js":3472,"./vnode.js":3473,"js-ext/extra/hashmap.js":3424,"js-ext/lib/object.js":3427,"js-ext/lib/promise.js":3428,"js-ext/lib/string.js":3429,"polyfill":3446,"polyfill/extra/transition.js":3441,"polyfill/extra/transitionend.js":3442,"polyfill/extra/vendorCSS.js":3443,"utils":3447,"window-ext":3453}],3470:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":3466,"./vdom-ns.js":3472,"js-ext/extra/hashmap.js":3424,"js-ext/lib/object.js":3427,"polyfill":3446}],3471:[function(require,module,exports){
arguments[4][97][0].apply(exports,arguments)
},{"./attribute-extractor.js":3466,"./vdom-ns.js":3472,"./vnode.js":3473,"js-ext/extra/hashmap.js":3424,"js-ext/lib/object.js":3427,"polyfill":3446}],3472:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":3424,"js-ext/lib/object.js":3427,"polyfill":3446}],3473:[function(require,module,exports){
"use strict";
/**
* Delivers the `vnode` prototype object, which is a virtualisation of an `Element` inside the Dom.
* These Elements work smoothless with the vdom (see ...).
*
* vnodes are much quicker to access and walk through than native dom-nodes. However, this is a module you don't need
* by itself: `Element`-types use these features under the hood.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* <br>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
*
* @module vdom
* @submodule vnode
* @class vnode
* @since 0.0.1
*/
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('js-ext/lib/string.js');
require('polyfill');
var createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.VNode) {
return window._ITSAmodules.VNode; // VNODE was already created
}
console.warn('VDOM 20');
var NS = require('./vdom-ns.js')(window),
extractor = require('./attribute-extractor.js')(window),
DOCUMENT = window.document,
LightMap = require('js-ext/extra/lightmap.js'),
MUTATION_EVENTS = new LightMap(),
BATCH_WILL_RUN = false,
xmlNS = NS.xmlNS,
nodeids = NS.nodeids,
htmlToVNodes = require('./html-parser.js')(window),
timers = require('utils/lib/timers.js'),
async = timers.async,
later = timers.later,
/*jshint proto:true */
PROTO_SUPPORTED = !!Object.__proto__,
/*jshint proto:false */
// cleanup memory after 1 minute: removed nodes SHOULD NOT be accessed afterwards
// because vnode would be recalculated and might be different from before
DESTROY_DELAY = 60000,
unescapeEntities = NS.UnescapeEntities,
NTH_CHILD_REGEXP = /^(?:(\d*)[n|N])([\+|\-](\d+))?$/, // an+b
STRING = 'string',
CLASS = 'class',
STYLE = 'style',
ID = 'id',
NODE= 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
EV_REMOVED = NODE+REMOVE,
EV_INSERTED = NODE+INSERT,
EV_CONTENT_CHANGE = NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = ATTRIBUTE+INSERT,
SPLIT_CHARACTER = createHashMap({
' ': true,
'>': true,
'+': true, // only select the element when it is immediately preceded by the former element
'~': true // only the element when it has the former element as a sibling. (just like `+`, but less strict)
}),
STORABLE_SPLIT_CHARACTER = createHashMap({
'>': true,
'+': true,
'~': true
}),
SIBLING_MATCH_CHARACTER = createHashMap({
'+': true,
'~': true
}),
ATTR_DETAIL_SPECIFIERS = createHashMap({
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to attribute-name end-tokens.
*
* @property END_ATTRIBUTENAME
* @default {
* '=': true,
* ']': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
END_ATTRIBUTENAME = createHashMap({
'=': true,
']': true,
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to different changes of Element nodeType changes.
*
* @property NODESWITCH
* @default {
* 1: {
* 1: 1,
* 3: 2,
* 8: 3
* },
* 3: {
* 1: 4,
* 3: 5,
* 8: 6
* },
* 8: {
* 1: 7,
* 3: 8,
* 8: 9
* }
* }
* @type Object
* @protected
* @since 0.0.1
*/
NODESWITCH = createHashMap({
1: createHashMap({
1: 1, // oldNodeType==Element, newNodeType==Element
3: 2, // oldNodeType==Element, newNodeType==TextNode
8: 3 // oldNodeType==Element, newNodeType==Comment
}),
3: createHashMap({
1: 4, // oldNodeType==TextNode, newNodeType==Element
3: 5, // oldNodeType==TextNode, newNodeType==TextNode
8: 6 // oldNodeType==TextNode, newNodeType==Comment
}),
8: createHashMap({
1: 7, // oldNodeType==Comment, newNodeType==Element
3: 8, // oldNodeType==Comment, newNodeType==TextNode
8: 9 // oldNodeType==Comment, newNodeType==Comment
})
}),
/**
* Object to gain quick access to selector start-tokens.
*
* @property SELECTOR_IDENTIFIERS
* @default {
* '#': 1,
* '.': 2,
* '[': 3
* }
* @type Object
* @protected
* @since 0.0.1
*/
SELECTOR_IDENTIFIERS = createHashMap({
'#': 1,
'.': 2,
'[': 3,
':': 4
}),
PSEUDO_FIRST_CHILD = ':first-child',
PSEUDO_FIRST_OF_TYPE = ':first-of-type',
PSEUDO_LAST_CHILD = ':last-child',
PSEUDO_LAST_OF_TYPE = ':last-of-type',
PSEUDO_NTH_CHILD = ':nth-child',
PSEUDO_NTH_LAST_CHILD = ':nth-last-child',
PSEUDO_NTH_LAST_OF_TYPE = ':nth-last-of-type',
PSEUDO_NTH_OF_TYPE = ':nth-of-type',
PSEUDO_ONLY_OF_TYPE = ':only-of-type',
PSEUDO_ONLY_CHILD = ':only-child',
/**
* Object to gain quick access to the selectors that required children
*
* @property PSEUDO_REQUIRED_CHILDREN
* @default {
* ':first-child': true,
* ':first-of-type': true,
* ':last-child': true,
* ':last-of-type': true,
* ':nth-child': true,
* ':nth-last-child': true,
* ':nth-last-of-type': true,
* ':nth-of-type': true,
* ':only-of-type': true,
* ':only-child': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
PSEUDO_REQUIRED_CHILDREN = createHashMap(),
_matchesSelectorItem, _matchesOneSelector, _findElementSibling, vNodeProto, _markRemoved, _tryReplaceChild,
_splitSelector, _findNodeSibling, _matchNthChild, _batchEmit, _emitDestroyChildren, _tryRemoveDomNode;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_CHILD] = true;
/**
* Searches for the next -or previous- node-sibling (nodeType of 1, 3 or 8).
*
* @method _findNodeSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findNodeSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
index = vParent.vChildNodes.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildNodes[index];
};
/**
* Searches for the next -or previous- Element-sibling (nodeType of 1).
*
* @method _findElementSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findElementSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
if (vnode.nodeType===1) {
index = vParent.vChildren.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildren[index];
}
else {
/*jshint noempty:true */
while ((vnode=_findNodeSibling(vnode, next)) && (vnode.nodeType!==1)) {}
/*jshint noempty:false */
return vnode;
}
};
/**
* Check whether the vnode matches a "nth-child" test, which is used for css pseudoselectors like `nth-child`, `nth-of-type` etc.
*
* @method _matchNthChild
* @param pseudoArg {String} the argument for nth-child
* @param index {Number} the index of the inspected vnode
* @return {Boolean} whether the vnode matches the nthChild test
* @protected
* @private
* @since 0.0.1
*/
_matchNthChild = function(pseudoArg, index) {
var match, k, a, b, nodeOk, nthIndex, sign, isNumber;
(pseudoArg==='even') && (pseudoArg='2n');
(pseudoArg==='odd') && (pseudoArg='2n+1');
match = pseudoArg.match(NTH_CHILD_REGEXP) || (isNumber=pseudoArg.validateNumber());
if (!match) {
return false;
}
// pseudoArg follows the pattern: `an+b`
if (!isNumber) {
a = match[1];
sign = match[2];
b = match[3] || 0;
(b==='') && (b=0);
}
else {
b = pseudoArg;
}
sign && (sign=sign[0]);
if (!a) {
// only fixed index to match
return (sign==='-') ? false : (parseInt(b, 10)===index);
}
else {
// we need to iterate
nodeOk = false;
b = window.Number(b);
for (k=0; !nodeOk; k++) {
nthIndex = (sign==='-') ? (a*k) - b : (a*k) + b;
if (nthIndex===index) {
nodeOk = true;
}
else if (nthIndex>index) {
// beyond index --> will never become a fix anymore
return false;
}
}
return nodeOk;
}
};
/**
* Check whether the vnode matches the css-selector. the css-selector should be a single selector,
* not multiple, so it shouldn't contain a `comma`.
*
* @method _matchesOneSelector
* @param vnode {vnode} the vnode to inspect
* @param selector {String} the selector-item to check the match for
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches the css-selector
* @protected
* @private
* @since 0.0.1
*/
_matchesOneSelector = function(vnode, selector, relatedVNode) {
var selList = _splitSelector(selector),
size = selList.length,
selMatch = false,
i, selectorItem, last,
rightvnode, relationMatch, checkRelation;
if (STORABLE_SPLIT_CHARACTER[selList[size-1]]) {
return false;
}
relationMatch = function(leftVNode, rightVNode, relationCharacter) {
var match, vParent, vChildren;
// when `selector` starts with `>`, `~` or `+`, then
// there should also be a match comparing a related node!
switch (relationCharacter) {
case '>':
leftVNode || (leftVNode=rightVNode.vParent);
match = (leftVNode.vChildren.indexOf(rightVNode)!==-1);
break;
case '~':
leftVNode || (leftVNode=rightVNode.vFirstElement);
vParent = leftVNode.vParent;
vChildren = vParent && vParent.vChildren;
match = vParent && (vChildren.indexOf(leftVNode)<vChildren.indexOf(rightVNode));
break;
case '+':
leftVNode || (leftVNode=rightVNode.vPreviousElement);
match = (leftVNode.vNextElement === rightVNode);
}
return !!match;
};
last = size - 1;
i = last;
while (vnode && ((selMatch || (i===last)) && (i>=0))) {
selectorItem = selList[i];
if (STORABLE_SPLIT_CHARACTER[selectorItem]) {
checkRelation = selectorItem;
i--;
}
else {
selMatch = _matchesSelectorItem(vnode, selectorItem);
if (!selMatch && (i===last)) {
return false;
}
while (!selMatch && vnode && (i!==last)) {
// may also look at nodes higher in the chain
vnode = SIBLING_MATCH_CHARACTER[selList[i+1]] ? vnode.vPreviousElement : vnode.vParent;
selMatch = vnode && _matchesSelectorItem(vnode, selectorItem);
}
selMatch && checkRelation && vnode && (selMatch=relationMatch(vnode, rightvnode, checkRelation));
rightvnode = vnode;
if (vnode) {
// do a precheck on the next checkRelation:
// and determine whether to set the vnode upper the tree or at the previous sibling:
vnode = SIBLING_MATCH_CHARACTER[selList[i-1]] ? vnode.vPreviousElement : vnode.vParent;
}
if (selMatch) {
checkRelation = false;
i--;
}
}
}
if ((rightvnode===relatedVNode) || (i!==-1)) {
selMatch = false;
}
selMatch && checkRelation && rightvnode && (selMatch=relationMatch(relatedVNode, rightvnode, checkRelation));
return selMatch;
};
/**
* Check whether the vnode matches one specific selector-item. Suppose the css-selector: "#mynode li.red .blue"
* then there are 3 selector-items: "#mynode", "li.red" and ".blue"
*
* This method also can handle the new selectors:
* <ul>
* <li>[att^=val] –-> the “begins with” selector</li>
* <li>[att$=val] –-> the “ends with” selector</li>
* <li>[att*=val] –-> the “contains” selector (might be a substring)</li>
* <li>[att~=val] –-> the “contains” selector as a separate word, separated by spaces</li>
* <li>[att|=val] –-> the “contains” selector as a separate word, separated by `|`</li>
* <li>+ --> (same level)</li>
* <li>~ --> (same level)</li>
* </ul>
*
* @method _matchesSelectorItem
* @param vnode {Object} the vnode to inspect
* @param selectorItem {String} the selector-item to check the match for
* @return {Boolean} whether the vnode matches the selector-item
* @protected
* @private
* @since 0.0.1
*/
_matchesSelectorItem = function (vnode, selectorItem) {
var i = 0,
len = selectorItem.length,
character = selectorItem[0],
tagName, id, className, attributeName, attributeValue, stringMarker, attributeisString, isBoolean, insideAttributeValue, insideAttribute,
vParent, checkBoolean, treatment, k, min, max, value, len2, index, found, pseudo, pseudoArg, arglevel, count, vParentVChildren;
if (selectorItem==='*') {
return true;
}
if (!SELECTOR_IDENTIFIERS[character]) {
// starts with tagName
tagName = '';
// reposition i to continue in the right way:
i--;
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
tagName += character;
}
if (tagName.toUpperCase()!==vnode.tag) {
return false;
}
}
while (i<len) {
switch (character) {
case '#':
id = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
id += character;
}
if (id!==vnode.id) {
return false;
}
break;
case '.':
className = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
className += character;
}
if (!vnode.hasClass(className)) {
return false;
}
break;
case '[':
attributeName = '';
while ((++i<len) && (character=selectorItem[i]) && !END_ATTRIBUTENAME[character]) {
attributeName += character;
}
// if character===']' then we have an attribute without a value-definition
if (!vnode.attrs[attributeName] || ((character===']') && (vnode.attrs[attributeName]!==''))) {
return !!vnode.attrs[attributeName];
}
// now we read the value of the attribute
// however, it could be that the selector has a special `detailed` identifier set (defined by: ATTR_DETAIL_SPECIFIERS)
if (ATTR_DETAIL_SPECIFIERS[character]) {
treatment = character; // store the character to know how the attributedata should be treaded
i++; // character should be a "=" by now
}
else {
treatment = null;
}
attributeValue = '';
stringMarker = selectorItem[i+1];
attributeisString = (stringMarker==='"') || (stringMarker==="'");
attributeisString && (i++);
// end of attributaValue = (character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker))
while ((++i<len) && (character=selectorItem[i]) && !((character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker)))) {
attributeValue += character;
}
if (attributeisString) {
// if attribute is string, then we need to _remove to last stringmarker
attributeValue = attributeValue.substr(0, attributeValue.length-1);
}
else {
// if attribute is no string, then we need to typecast its value
isBoolean = ((attributeValue.length>3) && (attributeValue.length<6) &&
(checkBoolean=attributeValue.toUpperCase()) &&
((checkBoolean==='FALSE') || (checkBoolean==='TRUE')));
// typecast the value to either Boolean or Number:
attributeValue = isBoolean ? (checkBoolean==='TRUE') : parseFloat(attributeValue);
}
// depending upon how the attributedata should be treated:
if (treatment) {
switch (treatment) {
case '^': // “begins with” selector
if (!vnode.attrs[attributeName].startsWith(attributeValue)) {
return false;
}
break;
case '$': // “ends with” selector
if (!vnode.attrs[attributeName].endsWith(attributeValue)) {
return false;
}
break;
case '*': // “contains” selector (might be a substring)
if (!vnode.attrs[attributeName].contains(attributeValue)) {
return false;
}
break;
case '~': // “contains” selector as a separate word, separated by spaces
if (!(' '+vnode.attrs[attributeName]+' ').contains(' '+attributeValue+' ')) {
return false;
}
break;
case '|': // “contains” selector as a separate word, separated by `|`
if (!('|'+vnode.attrs[attributeName]+'|').contains('|'+attributeValue+'|')) {
return false;
}
break;
}
}
else if (vnode.attrs[attributeName]!==attributeValue) {
return false;
}
// we still need to increase one position:
(++i<len) && (character=selectorItem[i]);
break;
case ':':
// we have a pseudo-selector
// first, find out which one
// because '::' is a valid start (though without any selection), we start to back the next character as well:
pseudo = ':'+selectorItem[++i];
pseudoArg = '';
vParent = vnode.vParent;
vParentVChildren = vParent && vParent.vChildren;
// pseudo-selectors might have an argument passed in, like `:nth-child(2n+1)` or `:not([type="checkbox"])` --> we
// store this argument inside `pseudoArg`
// also note that combinations are possible with `:not` --> `:not(:nth-child(2n+1))`
// also note that we cannot "just" look for a closing character when running into the usage of attributes:
// for example --> `:not([data-x="some data :)"])`
// that's why -once we are inside attribute-data- we need to continue until the attribute-data ends
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
if (character==='(') {
// starting arguments
arglevel = 1;
insideAttribute = false;
insideAttributeValue = false;
while ((++i<len) && (character=selectorItem[i]) && (arglevel>0)) {
if (!insideAttribute) {
if (character==='(') {
arglevel++;
}
else if (character===')') {
arglevel--;
}
else if (character==='[') {
insideAttribute = true;
}
}
else {
// inside attribute
if (!insideAttributeValue) {
if ((character==='"') || (character==="'")) {
insideAttributeValue = true;
stringMarker = character;
}
else if (character===']') {
insideAttribute = false;
}
}
else if ((character===stringMarker) && (selectorItem[i+1]===']')) {
insideAttributeValue = false;
}
}
(arglevel>0) && (pseudoArg+=character);
}
}
else {
pseudo += character;
}
}
// now, `pseudo` is known as well as its possible pseudoArg
if (!vParentVChildren && PSEUDO_REQUIRED_CHILDREN[pseudo]) {
return false;
}
switch (pseudo) {
case ':checked': // input:checked Selects every checked <input> element
if (!vnode.attrs.checked) {
return false;
}
break;
case ':disabled': // input:disabled Selects every disabled <input> element
if (!vnode.attrs.disabled) {
return false;
}
break;
case ':empty': // p:empty Selects every <p> element that has no children (including text nodes)
if (vnode.vChildNodes && (vnode.vChildNodes.length>0)) {
return false;
}
break;
case ':enabled': // input:enabled Selects every enabled <input> element
if (vnode.attrs.disabled) {
return false;
}
break;
case PSEUDO_FIRST_CHILD: // p:first-child Selects every <p> element that is the first child of its parent
if (vParentVChildren[0]!==vnode) {
return false;
}
break;
case PSEUDO_FIRST_OF_TYPE: // p:first-of-type Selects every <p> element that is the first <p> element of its parent
for (k=vParentVChildren.indexOf(vnode)-1; k>=0; k--) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':focus': // input:focus Selects the input element which has focus
if (vnode.domNode!==DOCUMENT.activeElement) {
return false;
}
break;
case ':in-range': // input:in-range Selects input elements with a value within a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || (value<min) || (value>max)) {
return false;
}
break;
case ':lang': // p:lang(it) Selects every <p> element with a lang attribute equal to "it" (Italian)
if (vnode.attrs.lang!==pseudoArg) {
return false;
}
break;
case PSEUDO_LAST_CHILD: // p:last-child Selects every <p> element that is the last child of its parent
if (vParentVChildren[vParentVChildren.length-1]!==vnode) {
return false;
}
break;
case PSEUDO_LAST_OF_TYPE: // p:last-of-type Selects every <p> element that is the last <p> element of its parent
len2 = vParentVChildren.length;
for (k=vParentVChildren.indexOf(vnode)+1; k<len2; k++) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':not': // :not(p) Selects every element that is not a <p> element
if (vnode.matchesSelector(pseudoArg)) {
return false;
}
break;
case PSEUDO_NTH_CHILD: // p:nth-child(2) Selects every <p> element that is the second child of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
index = vParentVChildren.indexOf(vnode)+1;
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_CHILD: // p:nth-last-child(2) Selects every <p> element that is the second child of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_OF_TYPE: // p:nth-last-of-type(2) Selects every <p> element that is the second <p> element of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
index = 0;
for (k=vParentVChildren.length-1; (k>=0) && !found; k--) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_OF_TYPE: // p:nth-of-type(2) Selects every <p> element that is the second <p> element of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
len2 = vParentVChildren.length;
index = 0;
for (k=0; (k<len2) && !found; k++) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_ONLY_OF_TYPE: // p:only-of-type Selects every <p> element that is the only <p> element of its parent
len2 = vParentVChildren.length;
count = 0;
for (k=0; (k<len2) && (count<=1); k++) {
(vParentVChildren[k].tag===vnode.tag) && count++;
}
if (count!==1) {
return false;
}
break;
case PSEUDO_ONLY_CHILD: // p:only-child Selects every <p> element that is the only child of its parent
if (vParentVChildren.length!==1) {
return false;
}
break;
case ':optional': // input:optional Selects input elements with no "required" attribute
if (vnode.attrs.required) {
return false;
}
break;
case ':out-of-range': // input:out-of-range Selects input elements with a value outside a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || ((value>=min) && (value<=max))) {
return false;
}
break;
case ':read-only': // input:read-only Selects input elements with the "readonly" attribute specified
if (!vnode.attrs.readonly) {
return false;
}
break;
case ':read-write': // input:read-write Selects input elements with the "readonly" attribute NOT specified
if (vnode.attrs.readonly) {
return false;
}
break;
case ':required': // input:required Selects input elements with the "required" attribute specified
if (!vnode.attrs.required) {
return false;
}
break;
case ':root': // Selects the document's root element
if (vnode.domNode!==DOCUMENT.documentElement) {
return false;
}
break;
}
}
}
return true;
};
/**
* Splits the selector into separate subselector-items that should match different elements through the tree.
* Special characters '>' and '+' are added as separate items in the hash.
*
* @method _splitSelector
* @param selector {String} the selector-item to check the match for
* @return {Array} splitted selectors
* @protected
* @private
* @since 0.0.1
*/
_splitSelector = function(selector) {
var list = [],
len = selector.length,
sel = '',
i, character, insideDataAttr;
for (i=0; i<len; i++) {
character = selector[i];
if (character==='[') {
sel += character;
insideDataAttr = true;
}
else if (character===']') {
sel += character;
insideDataAttr = false;
}
else if (insideDataAttr || !SPLIT_CHARACTER[character]) {
sel += character;
}
else {
// unique selectoritem is found, add it to the list
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
// in case the last character was '>', '+' or '~', we need to add it as a separate item
STORABLE_SPLIT_CHARACTER[character] && (list[list.length]=character);
}
}
// add the last item
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
return list;
};
_batchEmit = function() {
// we will exactly define the 'UI:'-event --> Itags will have another emitterName
MUTATION_EVENTS.each(function (mutationEvents, vnode) {
var domNode = vnode.domNode;
if (mutationEvents[EV_REMOVED]) {
domNode.emit('UI:'+EV_REMOVED);
}
else if (mutationEvents[EV_INSERTED]) {
domNode.emit('UI:'+EV_INSERTED);
}
else {
// contentchange and attributechanges can go hand in hand
mutationEvents.each(function(value, evt) {
domNode.emit('UI:'+evt, (evt===EV_CONTENT_CHANGE) ? null : {changed: value});
});
}
});
MUTATION_EVENTS.clear();
BATCH_WILL_RUN = false;
};
_emitDestroyChildren = function(vnode) {
var children = vnode.vChildren,
len = children.length,
i, vChild;
for (i=0; i<len; i++) {
vChild = children[i];
vChild._emit(EV_REMOVED);
}
};
_markRemoved = function(vnode) {
var vChildNodes = vnode.vChildNodes,
len, i, vChildNode;
if (vnode.nodeType===1) {
Object.protectedProp(vnode, 'removedFromDOM', true);
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && _markRemoved(vChildNode);
}
}
}
};
/**
* A safe way to remove a dom-node, even if it is not present.
* Will not throw a JS error when the "to be removed" node isn't in the dom
*
* @method _tryRemoveDomNode
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param childDomNode {DOMNode} the node that needs to be removed
* @since 0.0.1
*/
_tryRemoveDomNode = function(parentDomNode, childDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && childDomNode.isItag && childDomNode.isItag()) {
DOCUMENT._itagList.remove(childDomNode);
}
try {
parentDomNode._removeChild(childDomNode);
}
catch(err) {}
};
/**
* A safe way to replace a dom-node, even if the "to be replaced"-node it is not present.
* Will not throw a JS error when the "to be replaced" node isn't in the dom. in that case, the new node will
* be appended
*
* @method _tryReplaceChild
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param newChildDomNode {DOMNode} the node that needs to be replaced
* @param oldChildDomNode {DOMNode} the node that needs to be inserted
* @since 0.0.1
*/
_tryReplaceChild = function(parentDomNode, newChildDomNode, oldChildDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && oldChildDomNode.isItag && oldChildDomNode.isItag()) {
DOCUMENT._itagList.remove(oldChildDomNode);
}
try {
parentDomNode._replaceChild(newChildDomNode, oldChildDomNode);
}
catch(err) {
// if the childDomNode isn;t there any more - for whatever reason - the we need to append the newChildNode
parentDomNode._appendChild(newChildDomNode);
}
};
vNodeProto = window._ITSAmodules.VNode = {
/**
* Check whether the vnode's domNode is equal, or contains the specified Element.
*
* @method contains
* @return {Boolean} whether the vnode's domNode is equal, or contains the specified Element.
* @since 0.0.1
*/
contains: function(otherVNode, noProtectedSearch) {
var instance = this;
if (otherVNode && (otherVNode.destroyed || (noProtectedSearch && otherVNode._systemNode))) {
return false;
}
while (otherVNode && (otherVNode!==instance)) {
otherVNode = otherVNode.vParent;
if (otherVNode && noProtectedSearch && (otherVNode._systemNode || (otherVNode.isItag && otherVNode.domNode.contentHidden))) {
return false;
}
}
return (otherVNode===instance);
},
/**
* Empties the vnode.
*
* Syncs with the dom.
*
* @method empty
* @param [full=false] {Boolean} whether system-nodes should be removed as well
* @chainable
* @since 0.0.1
*/
empty: function(full) {
delete this._scripts;
return this._setChildNodes([], null, full);
},
/**
* Returns the first child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method firstOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the first child-vnode or null when not present
* @since 0.0.1
*/
firstOfVChildren: function(cssSelector) {
var instance = this,
found, i, len, vChildren, element;
if (!cssSelector) {
return instance.vFirstElementChild;
}
vChildren = instance.vChildren;
len = vChildren.length;
for (i=0; !found && (i<len); i++) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
return found;
},
/**
* Gets the innerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* Only valid for nodetype=1 (HTMLElements)
*
* @method getHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String|undefined} the innerHTML without the elements specified, or `undefined` when not an HTMLElement
* @since 0.0.1
*/
getHTML: function(exclude, includeSystemNodes) {
var instance = this,
html, vChildNodes, len, i, vChildNode;
if (instance.nodeType===1) {
if (exclude) {
Array.isArray(exclude) || (exclude=[exclude]);
}
html = '';
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
switch (vChildNode.nodeType) {
case 1:
(exclude && exclude.contains(vChildNode.domNode)) || (!includeSystemNodes && vChildNode._systemNode) || (html+=vChildNode.getOuterHTML(exclude, includeSystemNodes));
break;
case 3:
html += vChildNode.text.replace(/</g, '<').replace(/>/g, '>');
break;
case 8:
html += '<!--' + vChildNode.text.replace(/</g, '<').replace(/>/g, '>') + '-->';
}
}
}
return html;
},
/**
* Gets the outerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* @method getOuterHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String} the outerHTML
* @since 0.0.1
*/
getOuterHTML: function(exclude, includeSystemNodes) {
var instance = this,
html,
attrs = instance.attrs;
if (instance.nodeType===1) {
if (instance.nodeType!==1) {
return instance.textContent;
}
html = '<' + instance.tag.toLowerCase();
attrs.each(function(value, key) {
html += ' '+key+'="'+value+'"';
});
instance.isVoid && (html += '/');
html += '>';
if (!instance.isVoid) {
html += ((!includeSystemNodes && instance.isItag) ? '' : instance.getHTML(exclude, includeSystemNodes)) + '</' + instance.tag.toLowerCase() + '>';
}
}
return html;
},
/**
* Checks whether the vnode has any vChildNodes (nodeType of 1, 3 or 8).
*
* @method hasVChildNodes
* @return {Boolean} whether the vnode has any vChildNodes.
* @since 0.0.1
*/
hasVChildNodes: function() {
return this.vChildNodes ? (this.vChildNodes.length>0) : false;
},
/**
* Checks whether the vnode has any vChildren (vChildNodes with nodeType of 1).
*
* @method hasVChildren
* @return {Boolean} whether the vnode has any vChildren.
* @since 0.0.1
*/
hasVChildren: function() {
return this.vChildNodes ? (this.vChildren.length>0) : false;
},
/**
* Checks whether the className is present on the vnode.
*
* @method hasClass
* @param className {String|Array} the className to check for. May be an Array of classNames, which all needs to be present.
* @return {Boolean} whether the className (or classNames) is present on the vnode
* @since 0.0.1
*/
hasClass: function(className) {
var instance = this,
check = function(cl) {
return !!instance.classNames[cl];
};
if (!instance.classNames) {
return false;
}
if (typeof className === STRING) {
return check(className);
}
else if (Array.isArray(className)) {
return className.every(check);
}
return false;
},
/**
* Returns the last child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method lastOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the last child-vnode or null when not present
* @since 0.0.1
*/
lastOfVChildren: function(cssSelector) {
var vChildren = this.vChildren,
found, i, element;
if (vChildren) {
if (!cssSelector) {
return this.vLastElementChild;
}
for (i=vChildren.length-1; !found && (i>=0); i--) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
}
return found;
},
/**
* Checks whether the vnode matches one of the specified selectors. `selectors` can be one, or multiple css-selectors,
* separated by a `comma`. For example: "#myid li.red blue" is one selector, "div.red, div.blue, div.green" are three selectors.
*
* @method matchesSelector
* @param selectors {String} one or more css-selectors
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches one of the selectors
* @since 0.0.1
*/
matchesSelector: function(selectors, relatedVNode) {
var instance = this;
if (instance.nodeType!==1) {
return false;
}
selectors = selectors.split(',');
// we can use Array.some, because there won't be many separated selectoritems,
// so the final invocation won't be delayed much compared to looping
return selectors.some(function(selector) {
return _matchesOneSelector(instance, selector, relatedVNode);
});
},
/**
* Reloads the DOM-attribute into the vnode.
*
* @method matchesSelector
* @param attributeName {String} the name of the attribute to be reloaded.
* @return {Node} the domNode that was reloaded.
* @since 0.0.1
*/
reloadAttr: function(attributeName) {
var instance = this,
domNode = instance.domNode,
attributeValue = domNode._getAttribute(attributeName),
attrs = instance.attrs,
extractStyle, extractClass;
if (instance.nodeType===1) {
attributeValue || (attributeValue='');
if (attributeValue==='') {
delete attrs[attributeName];
// in case of STYLE attributeName --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attributeName --> special treatment
(attributeName===CLASS) && (instance.classNames={});
// in case of ID attributeName --> special treatment
if ((attributeName===ID) && (instance.id)) {
delete nodeids[instance.id];
delete instance.id;
}
}
else {
attrs[attributeName] = attributeValue;
// in case of STYLE attributeName --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(attributeValue);
attributeValue = extractStyle.attrStyle;
if (attributeValue) {
attrs.style = attributeValue;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attributeName --> special treatment
extractClass = extractor.extractClass(attributeValue);
attributeValue = extractClass.attrClass;
if (attributeValue) {
attrs[CLASS] = attributeValue;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (instance.id!==attributeValue) && (delete nodeids[instance.id]);
instance.id = attributeValue;
nodeids[attributeValue] = domNode;
}
}
}
return domNode;
},
/**
* Returns the vnode's style in a serialized form: the way it appears in the dom.
*
* @method serializeStyles
* @return {String} vnode's style
* @since 0.0.1
*/
serializeStyles: function() {
return extractor.serializeStyles(this.styles);
},
/**
* Sets the vnode's and dom-nodes inner HTML.
*
* Syncs with the dom. Can be invoked multiple times without issues.
*
* @method setHTML
* @param content {String} the innerHTML
* @param [suppressItagRender] {Boolean} to suppress Itags from rendering
* @param [allowScripts=false] {Boolean} whether scripts are allowed --> these should be defined with `xscript` instead of `script`
* @chainable
* @since 0.0.1
*/
setHTML: function(content, suppressItagRender, allowScripts) {
var instance = this;
instance._setChildNodes(htmlToVNodes(content, vNodeProto, instance.ns, null, suppressItagRender, allowScripts), suppressItagRender);
return instance;
},
/**
* Syncs the vnode's nodeid (if available) inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom. Can be invoked multiple times without issues.
*
* @method storeId
* @chainable
* @since 0.0.1
*/
storeId: function() {
// store node/vnode inside WeakMap:
var instance = this;
instance.id ? (nodeids[instance.id]=instance.domNode) : (delete nodeids[instance.id]);
return instance;
},
//---- private ------------------------------------------------------------------
_addToTaglist: function() {
var instance = this,
itagList;
if (instance.isItag) {
itagList = DOCUMENT.getItags(); // also reads the dom if the list isn't build yet
instance._data || Object.protectedProp(instance, '_data', {});
if (!instance._data.ce_destroyed && !itagList.contains(instance.domNode)) {
itagList.push(instance.domNode);
}
}
},
/**
* Adds a vnode to the end of the list of vChildNodes.
*
* Syncs with the DOM.
*
* @method _appendChild
* @param VNode {vnode} vnode to append
* @private
* @return {Node} the Node that was appended
* @since 0.0.1
*/
_appendChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
size, noProccessScript, scriptContent;
if ((VNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (VNode.attrs && VNode.attrs.src) ? VNode.attrs.src : VNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
VNode._moveToParent(instance);
instance.domNode._appendChild(domNode);
if (VNode.nodeType===3) {
size = instance.vChildNodes.length;
instance._normalize();
// if the size changed, then the domNode was merged
(size===instance.vChildNodes.length) || (domNode=instance.vChildNodes[instance.vChildNodes.length-1].domNode);
}
if (VNode.nodeType===1) {
VNode._addToTaglist();
VNode._emit(EV_INSERTED);
}
return domNode;
}
},
/**
* Removes the vnode from its parent vChildNodes- and vChildren-list.
*
* Does NOT sync with the dom.
*
* @method _deleteFromParent
* @private
* @chainable
* @since 0.0.1
*/
_deleteFromParent: function() {
var instance = this,
vParent = instance.vParent;
if (vParent && vParent.vChildNodes) {
vParent.vChildNodes.remove(instance);
// force to recalculate the vChildren on a next call:
(instance.nodeType===1) && (vParent._vChildren=null);
}
return instance;
},
/**
* Cleans up (empties) vnode's `_data`
*
* @method _cleanData
* @private
* @chainable
* @since 0.0.1
*/
_cleanData: function() {
var instance = this,
data = instance._data;
data && data.each(
function(value, key) {
delete data[key];
}
);
return instance;
},
/**
* Cleans up (removes) duplicated `style` definitions.
*
* @method _cleanupStyle
* @private
* @chainable
* @since 0.0.1
*/
_cleanupStyle: function() {
var instance = this,
compare = [],
removal = [],
vChildren = instance.vChildren,
len = vChildren.length,
vChild, i, styleContent, vChildInner;
for (i=0; i<len; i++) {
vChild = vChildren[i];
if (vChild.tag==='STYLE') {
vChildInner = vChild.vChildNodes[0];
if (vChildInner) {
styleContent = vChildInner.text;
if (compare.contains(styleContent)) {
removal[removal.length] = vChild;
}
else {
compare[compare.length] = styleContent;
}
}
else {
// empty style-tag
removal[removal.length] = vChild;
}
}
}
removal.forEach(function(vnode) {
instance._removeChild(vnode);
});
},
/**
* Destroys the vnode and all its vnode-vChildNodes.
* Removes it from its vParent.vChildNodes list,
* also removes its definitions inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom.
*
* @method _destroy
* @private
* @chainable
* @since 0.0.1
*/
_destroy: function(silent) {
var instance = this,
vChildNodes = instance.vChildNodes,
len, i, vChildNode, vParent, treeNodes;
if (!instance.destroyed) {
if (!silent) {
// Because we don't want to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
later(function() {
instance._emit(EV_REMOVED, null, null, null, true);
}, 5);
}
Object.protectedProp(instance, 'destroyed', true);
// first: determine the dom-tree, which module `event-dom` needs to determine where the node was before it was destroyed:
treeNodes = [instance];
vParent = instance.vParent;
while (vParent) {
treeNodes[treeNodes.length] = vParent;
vParent = vParent.vParent;
}
// mark all its vChildNodes so we can see if the node is in the DOM
_markRemoved(instance);
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && instance.isItag) {
DOCUMENT._itagList.remove(instance.domNode);
}
instance._scripts && (instance._scripts.length=0);
// The definite cleanup needs to be done after a timeout:
// someone might need to handle the Element when removed (fe to cleanup specific things)
later(function() {
instance._cleanData();
if (instance.nodeType===1) {
// _destroy all its vChildNodes
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && vChildNode._destroy(true);
}
}
}
instance._vChildren = null;
// explicitely set instance.domNode._vnode and instance.domNode to null in order to prevent problems with the GC (we break the circular reference)
delete instance.domNode._vnode;
// if valid id, then _remove the DOMnodeRef from internal hash
instance.id && delete nodeids[instance.id];
}, silent ? 0 : DESTROY_DELAY);
instance._deleteFromParent();
// Do not make domNode `null` --> it could be used even when not in the dom
}
return instance;
},
_emit: function(evt, attribute, newValue, prevValue, destroyEvt) {
/**
* Emitted by every Element that gets inserted.
*
* @event nodeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets removed.
*
* @event noderemove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets its content changed (innerHTML/innerText).
*
* @event nodecontentchange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute inserted.
*
* @event attributeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* </ul>
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute removed.
*
* @event attributeremove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Strings of the attributeNames that are removed
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute changed.
*
* @event attributechange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* <li>prevValue</li>
* </ul>
* @since 0.1
*/
var instance = this,
silent, attrMutations, mutationEvents, mutation, vParent;
if (!DOCUMENT.hasMutationSubs || (instance.nodeType!==1)) {
return;
}
silent = !!DOCUMENT._suppressMutationEvents;
if (!silent && (destroyEvt || !instance.destroyed)) {
// Because we don't wannt to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
mutationEvents = MUTATION_EVENTS.get(instance) || {};
if (attribute) {
attrMutations = mutationEvents[evt] || [];
if (evt===EV_ATTRIBUTE_REMOVED) {
mutation = attribute;
}
else {
mutation = {
attribute: attribute
};
if ((evt===EV_ATTRIBUTE_INSERTED) || (evt===EV_ATTRIBUTE_CHANGED)) {
mutation.newValue = newValue;
}
if ((evt===EV_ATTRIBUTE_CHANGED) && prevValue) {
mutation.prevValue = prevValue;
}
}
attrMutations.push(mutation);
mutationEvents[evt] = attrMutations;
}
else {
mutationEvents[evt] = true;
}
MUTATION_EVENTS.set(instance, mutationEvents);
// now set all parent to have a nodecontentchange:
vParent = instance.vParent;
vParent && vParent._emit(EV_CONTENT_CHANGE);
// in case of removal we need to emit EV_REMOVED for all children right now
// for they will be actually removed silently after a delay of 1 minute
(evt===EV_REMOVED) && _emitDestroyChildren(instance);
if (!BATCH_WILL_RUN) {
BATCH_WILL_RUN = true;
async(function() {
_batchEmit();
});
}
}
return instance;
},
/**
* Inserts `newVNode` before `refVNode`.
*
* Syncs with the DOM.
*
* @method _insertBefore
* @param newVNode {vnode} vnode to insert
* @param refVNode {vnode} The vnode before which newVNode should be inserted.
* @private
* @return {Node} the Node being inserted (equals domNode)
* @since 0.0.1
*/
_insertBefore: function(newVNode, refVNode) {
var instance = this,
domNode = newVNode.domNode,
index = instance.vChildNodes.indexOf(refVNode),
noProccessScript, scriptContent;
if ((newVNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (newVNode.attrs && newVNode.attrs.src) ? newVNode.attrs.src : newVNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
if (index!==-1) {
newVNode._moveToParent(instance, index);
instance.domNode._insertBefore(domNode, refVNode.domNode);
(newVNode.nodeType===3) && instance._normalize();
if (newVNode.nodeType===1) {
newVNode._addToTaglist();
newVNode._emit(EV_INSERTED);
}
return domNode;
}
else {
console.warn('trying to insert before, but no ref-node found. Will append the new node');
return instance._appendChild(newVNode);
}
}
},
/**
* Moves the vnode from its current parent.vChildNodes list towards a new parent vnode at the specified position.
*
* Does NOT sync with the dom.
*
* @method _moveToParent
* @param parentVNode {vnode} the parent-vnode
* @param [index] {Number} the position of the child. When not specified, it will be appended.
* @private
* @chainable
* @since 0.0.1
*/
_moveToParent: function(parentVNode, index) {
var instance = this,
vParent = instance.vParent;
instance._deleteFromParent();
instance.vParent = parentVNode;
parentVNode.vChildNodes || (parentVNode.vChildNodes=[]);
(typeof index==='number') ? parentVNode.vChildNodes.insertAt(instance, index) : (parentVNode.vChildNodes[parentVNode.vChildNodes.length]=instance);
// force to recalculate the vChildren on a next call:
vParent && (instance.nodeType===1) && (vParent._vChildren = null);
// force to recalculate the vChildren on a next call:
parentVNode && (instance.nodeType===1) && (parentVNode._vChildren=null);
return instance;
},
/**
* Removes empty TextNodes and merges following TextNodes inside the vnode.
*
* Syncs with the dom.
*
* @method _normalize
* @private
* @chainable
* @since 0.0.1
*/
_normalize: function() {
var instance = this,
domNode = instance.domNode,
vChildNodes = instance.vChildNodes,
changed = false,
i, preChildNode, vChildNode;
if (!instance._unNormalizable && vChildNodes) {
for (i=vChildNodes.length-1; i>=0; i--) {
vChildNode = vChildNodes[i];
preChildNode = vChildNodes[i-1]; // i will get the value `-1` eventually, which leads into undefined preChildNode
if (vChildNode.nodeType===3) {
if (vChildNode.text==='') {
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
else if (preChildNode && preChildNode.nodeType===3) {
preChildNode.text += vChildNode.text;
preChildNode.domNode.nodeValue = unescapeEntities(preChildNode.text);
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
}
}
}
changed && instance._emit(EV_CONTENT_CHANGE);
return instance;
},
/**
* Makes the vnode `normalizable`. Could be set to `false` when batch-inserting nodes, while `normalizaing` manually at the end.
* Afterwards, you should always reset `normalizable` to true.
*
* @method _normalizable
* @param value {Boolean} whether the vnode should be normalisable.
* @private
* @chainable
* @since 0.0.1
*/
_normalizable: function(value) {
var instance = this;
value ? (delete instance._unNormalizable) : (instance._unNormalizable=true);
return instance;
},
/**
* Prevents MutationObserver from making the dom sync with the vnode.
* Should be used when manipulating the dom from within the vnode itself (to preventing looping)
*
* @method _noSync
* @chainable
* @private
* @since 0.0.1
*/
_noSync: function() {
var instance = this;
if (!instance._nosync) {
instance._nosync = true;
async(function() {
instance._nosync = false;
});
}
return instance;
},
/**
* Removes the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _removeAttr
* @param attributeName {String}
* @private
* @chainable
* @since 0.0.1
*/
_removeAttr: function(attributeName, suppressItagRender) {
var instance = this,
attributeNameSplitted, ns;
if (instance.isItag && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
console.warn('Not allowed to remove the attribute '+attributeName);
return instance;
}
if (instance.attrs[attributeName]!==undefined) {
delete instance.attrs[attributeName];
// in case of STYLE attribute --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attribute --> special treatment
(attributeName===CLASS) && (instance.classNames={});
if (attributeName===ID) {
delete nodeids[instance.id];
delete instance.id;
}
instance._emit(EV_ATTRIBUTE_REMOVED, attributeName);
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
instance.domNode._removeAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName);
}
else {
instance.domNode._removeAttribute(attributeName);
}
}
return instance;
},
/**
* Removes the vnode's child-vnode from its vChildren and the DOM.
*
* Syncs with the DOM.
*
* @method removeChild
* @param VNode {vnode} the child-vnode to remove
* @private
* @since 0.0.1
*/
_removeChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
hadFocus = domNode && domNode.hasFocus && domNode.hasFocus() && (VNode.attrs['fm-lastitem']==='true'),
parentVNode = VNode.vParent;
VNode._destroy();
_tryRemoveDomNode(instance.domNode, VNode.domNode);
instance._normalize();
// now, reset the focus on focusmanager when needed:
if (hadFocus) {
while (parentVNode && !parentVNode.attrs['fm-manage']) {
parentVNode = parentVNode.vParent;
}
parentVNode && parentVNode.domNode.focus();
}
},
/**
* Replaces the current vnode at the parent.vChildNode list by `newVNode`
*
* Does NOT sync with the dom.
*
* @method _replaceAtParent
* @param newVNode {Object} the new vnode which should take over the place of the current vnode
* @private
* @chainable
* @since 0.0.1
*/
_replaceAtParent: function(newVNode) {
var instance = this,
vParent = instance.vParent,
vChildNodes, index;
if (vParent && (vChildNodes=vParent.vChildNodes)) {
index = vChildNodes.indexOf(instance);
// force to recalculate the vChildren on a next call:
((instance.nodeType===1) || (newVNode.nodeType===1)) && (instance.vParent._vChildren=null);
vChildNodes[index] = newVNode;
}
return instance._destroy();
},
/**
* Sets the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _setAttr
* @param attributeName {String}
* @param value {String} the value for the attributeName
* @param [force=false] {Boolean} force the attribute to be set, even if restrictions would deny it
* @private
* @chainable
* @since 0.0.1
*/
_setAttr: function(attributeName, value, force, suppressItagRender) {
var instance = this,
extractStyle, extractClass,
attrs = instance.attrs,
prevVal = attrs[attributeName],
domNode = instance.domNode,
attributeNameSplitted, ns;
if (instance.isItag && !force && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
if (prevVal!=value) {
console.warn('Not allowed to set the attribute '+attributeName);
}
return instance;
}
// don't check by !== --> value isn't parsed into a String yet
if (prevVal && ((value===undefined) || (value===null))) {
instance._removeAttr(attributeName, suppressItagRender);
return instance;
}
// attribute-values are always Strings:
value = String(value);
// attribute-values will be stored without " or '
value = value.replace(/"/g, '"').replace(/'/g, "'");
if (prevVal!=value) {
attrs[attributeName] = value;
// in case of STYLE attribute --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(value);
value = extractStyle.attrStyle;
if (value) {
attrs.style = value;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attribute --> special treatment
extractClass = extractor.extractClass(value);
value = extractClass.attrClass;
if (value) {
attrs[CLASS] = value;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (delete nodeids[instance.id]);
instance.id = value;
nodeids[value] = domNode;
}
instance._emit(prevVal ? EV_ATTRIBUTE_CHANGED : EV_ATTRIBUTE_INSERTED, attributeName, value, prevVal);
// when set in the dom --> quotes need to be set as "
value = value.replace(/"/g, '"');
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
domNode._setAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName, value);
}
else {
domNode._setAttribute(attributeName, value);
}
}
return instance;
},
/**
* Redefines the attributes of both the vnode as well as its related dom-node. The new
* definition replaces any previous attributes (without touching unmodified attributes).
* the `is` attribute cannot be changed for itags.
*
* Syncs the new vnode's attributes with the dom.
*
* @method _setAttrs
* @param newAttrs {Object|Array} the new attributes to be set
* @private
* @chainable
* @since 0.0.1
*/
_setAttrs: function(newAttrs, suppressItagRender) {
// does sync the DOM
var instance = this,
attrsObj, attr, attrs, i, key, keys, len, value;
if (instance.nodeType!==1) {
return;
}
instance._noSync();
attrs = instance.attrs;
attrs.id && (delete nodeids[attrs.id]);
if (Object.isObject(newAttrs)) {
attrsObj = newAttrs;
}
else {
attrsObj = {};
len = newAttrs.length;
for (i=0; i<len; i++) {
attr = newAttrs[i];
attrsObj[attr.name] = attr.value;
}
}
if (instance.isItag && !suppressItagRender) {
if (attrs.is) {
attrsObj.is = attrs.is;
}
else {
delete attrsObj.is;
delete attrsObj.Is;
delete attrsObj.iS;
delete attrsObj.IS;
}
}
// first _remove the attributes that are no longer needed.
// quickest way for object iteration: http://jsperf.com/object-keys-iteration/20
keys = Object.keys(attrs);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
attrsObj[key] || instance._removeAttr(key, suppressItagRender);
}
// next: every attribute that differs: redefine
keys = Object.keys(attrsObj);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
value = attrsObj[key];
(attrs[key]===value) || instance._setAttr(key, value, suppressItagRender);
}
return instance;
},
/**
* Redefines the childNodes of both the vnode as well as its related dom-node. The new
* definition replaces any previous nodes. (without touching unmodified nodes).
*
* Syncs the new vnode's childNodes with the dom.
*
* @method _setChildNodes
* @param newVChildNodes {Array} array with vnodes which represent the new childNodes
* @private
* @chainable
* @since 0.0.1
*/
_setChildNodes: function(newVChildNodes, suppressItagRender, removeSystemElements) {
// does sync the DOM
var instance = this,
vChildNodes = instance.vChildNodes || [],
domNode = instance.domNode,
forRemoval = [],
i, oldChild, newChild, newLength, len, len2, childDomNode, nodeswitch, bkpAttrs, cleanupStyle,
process, bkpChildNodes, needNormalize, prevSuppress, scriptContent, _scripts, scriptLen, j;
instance._noSync();
// first: reset ._vChildren --> by making it empty, its getter will refresh its list on a next call
instance._vChildren = null;
// if newVChildNodes is undefined, then we assume it to be empty --> an empty array
newVChildNodes || (newVChildNodes=[]);
// quickest way to loop through array is by using for loops: http://jsperf.com/array-foreach-vs-for-loop/5
len = vChildNodes.length;
// we need to add the systemNodes -if any- to the new newVChildNodes: they should be retained.
// SystemNodes are always places at the beginning, so they won't get reshuffled:
if (!removeSystemElements) {
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
if (oldChild._systemNode) {
newVChildNodes.insertAt(oldChild, i);
}
else {
break;
}
}
}
newLength = newVChildNodes.length;
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
childDomNode = oldChild.domNode;
if (i < newLength) {
newChild = newVChildNodes[i];
newChild.vParent || (newChild.vParent=instance);
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
/*jshint boss:true */
switch (nodeswitch=NODESWITCH[oldChild.nodeType][newChild.nodeType]) {
/*jshint boss:false */
case 1: // oldNodeType==Element, newNodeType==Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
if ((oldChild.tag!==newChild.tag) ||
((oldChild.tag===newChild.tag) && oldChild.isItag && (oldChild.attrs.is!==newChild.attrs.is))) {
// new tag --> completely replace
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
/*jshint proto:true */
oldChild.isItag && oldChild.domNode.destroyUI && oldChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
newChild.attrs = {}; // reset to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
oldChild._replaceAtParent(newChild);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
else {
// same tag --> only update what is needed
// NOTE: when this._unchangableAttrs exists, an itag-element syncs its UI -->
if (oldChild._data) {
// we might need to set the class `itag-rendered` when the attributeData says so:
// this happens when an itag gets refreshed with an unrendered definition
if (oldChild._data.itagRendered && !newChild.hasClass('itag-rendered')) {
newChild.classNames['itag-rendered'] = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' '+'itag-rendered';
}
else {
newChild.attrs[CLASS] = 'itag-rendered';
}
}
// we might need to set the class `focussed` when the attributeData says so:
// this happens when an itag gets rerendered: its renderFn doesn't know if any elements
// were focussed
if (oldChild._data.focussed && !newChild.hasClass('focussed')) {
newChild.classNames.focussed = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' ' + 'focussed';
}
else {
newChild.attrs[CLASS] = 'focussed';
}
}
if (oldChild._data['fm-tabindex']) {
// node has the tabindex set by the focusmanager,
// but that info might got lost with re-rendering of the new element
newChild.attrs.tabindex = '0';
}
}
if (oldChild.isItag) {
prevSuppress = DOCUMENT._suppressMutationEvents || false;
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
/*jshint proto:true */
newChild.domNode.destroyUI && newChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
oldChild._setAttrs(newChild.attrs, suppressItagRender);
newChild._destroy(true); // destroy through the vnode and removing from DOCUMENT._itagList
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(prevSuppress);
}
else {
oldChild._setAttrs(newChild.attrs, suppressItagRender);
// next: sync the vChildNodes:
oldChild._setChildNodes(newChild.vChildNodes, suppressItagRender);
}
// reset ref. to the domNode, for it might have been changed by newChild:
oldChild.id && (nodeids[oldChild.id]=childDomNode);
newVChildNodes[i] = oldChild;
}
}
break;
case 2: // oldNodeType==Element, newNodeType==TextNode
// case2 and case3 should be treated the same
case 3: // oldNodeType==Element, newNodeType==Comment
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
oldChild._replaceAtParent(newChild);
instance._emit(EV_CONTENT_CHANGE);
break;
case 4: // oldNodeType==TextNode, newNodeType==Element
// case4 and case7 should be treated the same
case 7: // oldNodeType==Comment, newNodeType==Element
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
// oldChild.isVoid = newChild.isVoid;
// delete oldChild.text;
instance._emit(EV_CONTENT_CHANGE);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
break;
case 5: // oldNodeType==TextNode, newNodeType==TextNode
// case5 and case9 should be treated the same
case 9: // oldNodeType==Comment, newNodeType==Comment
if (oldChild.text!==newChild.text) {
oldChild.text = newChild.text;
oldChild.domNode.nodeValue = unescapeEntities(newChild.text);
instance._emit(EV_CONTENT_CHANGE);
}
newVChildNodes[i] = oldChild;
break;
case 6: // oldNodeType==TextNode, newNodeType==Comment
// case6 and case8 should be treated the same
case 8: // oldNodeType==Comment, newNodeType==TextNode
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = oldChild.vParent;
instance._emit(EV_CONTENT_CHANGE);
}
if ((nodeswitch===2) || (nodeswitch===5) || (nodeswitch===8)) {
needNormalize = true;
}
}
else {
// _remove previous definition
_tryRemoveDomNode(domNode, oldChild.domNode);
// the oldChild needs to be removed, however, this cannot be done right now, for it would effect the loop
// so we store it inside a hash to remove it later
forRemoval[forRemoval.length] = oldChild;
}
}
// now definitely remove marked childNodes:
len2 = forRemoval.length;
for (i=0; i<len2; i++) {
forRemoval[i]._destroy();
}
// now we add all new vChildNodes that go beyond `len`:
for (i = len; i < newLength; i++) {
newChild = newVChildNodes[i];
newChild.vParent = instance;
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
switch (newChild.nodeType) {
case 1: // Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
domNode._appendChild(newChild.domNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
break;
case 3: // TextNode
needNormalize = true;
// we need to break through --> no `break`
/* falls through */
default: // TextNode or CommentNode
// newChild.domNode.nodeValue = newChild.text;
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
domNode._appendChild(newChild.domNode);
instance._emit(EV_CONTENT_CHANGE);
}
newChild.storeId();
}
instance.vChildNodes = newVChildNodes;
needNormalize && instance._normalize();
cleanupStyle && instance._cleanupStyle();
return instance;
},
_setUnchangableAttrs: function(unchangableObj) {
this._unchangableAttrs = unchangableObj;
}
};
//---- properties ------------------------------------------------------------------
/**
* A hash of all the `attributes` of the vnode's representing dom-node.
*
* @property attrs
* @type Object
* @since 0.0.1
*/
/**
* Hash with all the classes of the vnode. Every class represents a key, all values are set `true`.
*
* @property classNames
* @type Object
* @since 0.0.1
*/
/**
* The `id` of the vnode's representing dom-node (if any).
*
* @property id
* @type String
* @since 0.0.1
*/
/**
* Tells whether tag is a void Element. Examples are: `br`, `img` and `input`. Non-void Elements are f.e. `div` and `table`.
* For TextNodes and CommentNodes, this property is `undefined`.
*
* @property isVoid
* @type Boolean
* @since 0.0.1
*/
/**
* The `nodeType` of the vnode's representing dom-node (1===ElementNode, 3===TextNode, 8===CommentNode).
*
* @property nodeType
* @type Number
* @since 0.0.1
*/
/**
* The `tag` of the vnode's representing dom-node (allways uppercase).
*
* @property tag
* @type String
* @since 0.0.1
*/
/**
* The `content` of the vnode's representing dom-node, in case it is a TextNode or CommentNode.
* Equals dom-node.nodeValue.
*
* Is `undefined` for ElementNodes.
*
* @property text
* @type String
* @since 0.0.1
*/
/**
* Hash with all the childNodes (vnodes). vChildNodes are any kind of vnodes (nodeType===1, 3 or 8)
*
* @property vChildNodes
* @type Array
* @since 0.0.1
*/
/**
* The underlying `dom-node` that the vnode represents.
*
* @property domNode
* @type domNode
* @since 0.0.1
*/
/**
* vnode's parentNode (defined as a vnode itself).
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
Object.defineProperties(vNodeProto, {
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property innerHTML
* @type String
* @since 0.0.1
*/
innerHTML: {
get: function() {
return this.getHTML();
},
set: function(v) {
this.setHTML(v);
}
},
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property nodeValue
* @type String
* @since 0.0.1
*/
nodeValue: {
get: function() {
var instance = this;
return ((instance.nodeType===3) || (instance.nodeType===8)) ? instance.text : null;
},
set: function(v) {
var instance = this,
newTextContent, prevTextContent;
if ((instance.nodeType===3) || (instance.nodeType===8)) {
prevTextContent = instance.domNode.textContent;
instance.domNode.textContent = v;
// set .text AFTER the dom-node is updated --> the content might be escaped!
newTextContent = instance.text = instance.domNode.textContent;
(newTextContent!==prevTextContent) && instance._emit(EV_CONTENT_CHANGE);
}
}
},
/**
* Gets or sets the outerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property outerHTML
* @type String
* @since 0.0.1
*/
outerHTML: {
get: function() {
return this.getOuterHTML();
},
set: function(v) {
var instance = this,
vParent = instance.vParent,
id = instance.attrs.id,
vnode, vnodes, bkpAttrs, bkpChildNodes, i, len, vChildNodes, isLastChildNode, index, refDomNode;
if ((instance.nodeType!==1) || !vParent) {
return;
}
instance._noSync();
vChildNodes = vParent.vChildNodes;
index = vChildNodes.indexOf(instance);
isLastChildNode = (index===(vChildNodes.length-1));
isLastChildNode || (refDomNode=vChildNodes[index+1].domNode);
vnodes = htmlToVNodes(v, vNodeProto, vParent.ns, vParent);
len = vnodes.length;
if (len>0) {
// the first vnode will replace the current instance:
vnode = vnodes[0];
if (vnode.nodeType===1) {
if (vnode.tag!==instance.tag) {
// new tag --> completely replace
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
id && (delete nodeids[id]);
vnode.attrs = {}; // reset to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
// vnode.attrs = bkpAttrs;
// vnode.vChildNodes = bkpChildNodes;
vnode.id && (nodeids[vnode.id]=vnode.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
else {
instance._setAttrs(vnode.attrs);
instance._setChildNodes(vnode.vChildNodes);
}
}
else {
id && (delete nodeids[id]);
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
}
for (i=1; i<len; i++) {
vnode = vnodes[i];
switch (vnode.nodeType) {
case 1: // Element
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
vnode.attrs = {}; // reset, to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._insertBefore(vnode.domNode, refDomNode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
break;
default: // TextNode or CommentNode
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._appendChild(vnode.domNode, refDomNode);
}
vnode.storeId();
vnode._moveToParent(vParent, index+i);
}
}
},
/**
* Gets or sets the innerContent of the Node as plain text.
*
* The setter syncs with the DOM.
*
* @property textContent
* @type String
* @since 0.0.1
*/
textContent: {
get: function() {
var instance = this,
text = '',
vChildNodes = instance.vChildNodes,
len, i, vChildNode;
if (instance.nodeType===1) {
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
text += (vChildNode.nodeType===3) ? vChildNode.text : ((vChildNode.nodeType===1) ? vChildNode.textContent : '');
}
}
else {
text = instance.text;
}
return text;
},
set: function(v) {
var vnode = Object.create(vNodeProto);
vnode.domNode = DOCUMENT.createTextNode(v);
// create circular reference:
vnode.domNode._vnode = vnode;
vnode.nodeType = 3;
vnode.text = vnode.domNode.textContent;
this._setChildNodes([vnode]);
}
},
/**
* Hash with all the children (vnodes). vChildren are vnodes that have a representing dom-node that is an HtmlElement (nodeType===1)
*
* @property vChildren
* @type Array
* @since 0.0.1
*/
vChildren: {
get: function() {
var instance = this,
children = instance._vChildren,
vChildNode, vChildNodes, i, len;
vChildNodes = instance.vChildNodes;
if (vChildNodes && !children) {
children = instance._vChildren = [];
len = vChildNodes.length;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
(vChildNode.nodeType===1) && (children[children.length]=vChildNode);
}
}
children || (children = instance._vChildren = []);
return children;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirst
* @type vnode
* @since 0.0.1
*/
vFirst: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstChild;
}
},
/**
* Reference to the first vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirstChild
* @type vnode
* @since 0.0.1
*/
vFirstChild: {
get: function() {
return (this.vChildNodes && this.vChildNodes[0]) || null;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vFirstElement
* @type vnode
* @since 0.0.1
*/
vFirstElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstElementChild;
}
},
/**
* Reference to the first vChild, where the related dom-node an Element (nodeType===1).
*
* @property vFirstElementChild
* @type vnode
* @since 0.0.1
*/
vFirstElementChild: {
get: function() {
return this.vChildren[0] || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLast
* @type vnode
* @since 0.0.1
*/
vLast: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastChild;
}
},
/**
* Reference to the last vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLastChild
* @type vnode
* @since 0.0.1
*/
vLastChild: {
get: function() {
var vChildNodes = this.vChildNodes;
return (vChildNodes && vChildNodes[vChildNodes.length-1]) || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vLastElement
* @type vnode
* @since 0.0.1
*/
vLastElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastElementChild;
}
},
/**
* Reference to the last vChild, where the related dom-node an Element (nodeType===1).
*
* @property vLastElementChild
* @type vnode
* @since 0.0.1
*/
vLastElementChild: {
get: function() {
var vChildren = this.vChildren;
return vChildren[vChildren.length-1] || null;
}
},
/**
* the Parent vnode
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
/**
* Reference to the next of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vNext
* @type vnode
* @since 0.0.1
*/
vNext: {
get: function() {
return _findNodeSibling(this, true);
}
},
/**
* Reference to the next of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vNextElement
* @type vnode
* @since 0.0.1
*/
vNextElement: {
get: function() {
return _findElementSibling(this, true);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vPrevious
* @type vnode
* @since 0.0.1
*/
vPrevious: {
get: function() {
return _findNodeSibling(this);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vPreviousElement
* @type vnode
* @since 0.0.1
*/
vPreviousElement: {
get: function() {
return _findElementSibling(this);
}
}
});
return vNodeProto;
};
},{"./attribute-extractor.js":3466,"./html-parser.js":3470,"./vdom-ns.js":3472,"js-ext/extra/hashmap.js":3424,"js-ext/extra/lightmap.js":3425,"js-ext/lib/array.js":3426,"js-ext/lib/object.js":3427,"js-ext/lib/string.js":3429,"polyfill":3446,"utils/lib/timers.js":3449}],3474:[function(require,module,exports){
arguments[4][100][0].apply(exports,arguments)
},{"./partials/extend-document.js":3468,"./partials/extend-element.js":3469,"./partials/node-parser.js":3471,"js-ext/extra/hashmap.js":3424,"js-ext/lib/object.js":3427,"utils/lib/timers.js":3449}],3475:[function(require,module,exports){
(function (process,Buffer){
"use strict";
/**
* Wrapper for built-in http.js to emulate the browser XMLHttpRequest object.
*
* This can be used with JS designed for browsers to improve reuse of code and
* allow the use of existing libraries.
*
* Usage: include("XMLHttpRequest.js") and use XMLHttpRequest per W3C specs.
*
* @author Dan DeFelippi <dan@driverdan.com>
* @contributor David Ellis <d.f.ellis@ieee.org>
* @license MIT
*/
var Url = require("url"),
spawn = require("child_process").spawn,
fs = require('fs'),
XmlDOMParser = require('xmldom').DOMParser;
exports.XMLHttpRequest = function() {
/**
* Private variables
*/
var self = this;
var http = require('http');
var https = require('https');
// Holds http.js objects
var request;
var response;
// Request settings
var settings = {};
// Disable header blacklist.
// Not part of XHR specs.
var disableHeaderCheck = false;
// Set some default headers
var defaultHeaders = {
"User-Agent": "node-XMLHttpRequest",
"Accept": "*/*"
};
var headers = defaultHeaders;
// These headers are not user setable.
// The following are allowed but banned in the spec:
// * user-agent
var forbiddenRequestHeaders = [
"accept-charset",
"accept-encoding",
"access-control-request-headers",
"access-control-request-method",
"connection",
"content-length",
"content-transfer-encoding",
"cookie",
"cookie2",
"date",
"expect",
"host",
"keep-alive",
"origin",
"referer",
"te",
"trailer",
"transfer-encoding",
"upgrade",
"via"
];
// These request methods are not allowed
var forbiddenRequestMethods = [
"TRACE",
"TRACK",
"CONNECT"
];
// Send flag
var sendFlag = false;
// Error flag, used when errors occur or abort is called
var errorFlag = false;
// Event listeners
var listeners = {};
/**
* Constants
*/
this.UNSENT = 0;
this.OPENED = 1;
this.HEADERS_RECEIVED = 2;
this.LOADING = 3;
this.DONE = 4;
/**
* Public vars
*/
// Current state
this.readyState = this.UNSENT;
// default ready state change handler in case one is not set or is set late
this.onreadystatechange = null;
// Result & response
this.responseText = "";
this.responseXML = null;
this.status = null;
this.statusText = null;
/**
* Private methods
*/
var isXMLRequest = function() {
return /^text\/xml/.test(response.headers['content-type']);
};
/**
* Check if the specified header is allowed.
*
* @param string header Header to validate
* @return boolean False if not allowed, otherwise true
*/
var isAllowedHttpHeader = function(header) {
return disableHeaderCheck || (header && forbiddenRequestHeaders.indexOf(header.toLowerCase()) === -1);
};
/**
* Check if the specified method is allowed.
*
* @param string method Request method to validate
* @return boolean False if not allowed, otherwise true
*/
var isAllowedHttpMethod = function(method) {
return (method && forbiddenRequestMethods.indexOf(method) === -1);
};
/**
* Public methods
*/
/**
* Open the connection. Currently supports local server requests.
*
* @param string method Connection method (eg GET, POST)
* @param string url URL for the connection.
* @param boolean async Asynchronous connection. Default is true.
* @param string user Username for basic authentication (optional)
* @param string password Password for basic authentication (optional)
*/
this.open = function(method, url, async, user, password) {
this.abort();
errorFlag = false;
// Check for valid request method
if (!isAllowedHttpMethod(method)) {
throw "SecurityError: Request method not allowed";
}
settings = {
"method": method,
"url": url.toString(),
"async": (typeof async !== "boolean" ? true : async),
"user": user || null,
"password": password || null
};
setState(this.OPENED);
};
/**
* Disables or enables isAllowedHttpHeader() check the request. Enabled by default.
* This does not conform to the W3C spec.
*
* @param boolean state Enable or disable header checking.
*/
this.setDisableHeaderCheck = function(state) {
disableHeaderCheck = state;
};
/**
* Sets a header for the request.
*
* @param string header Header name
* @param string value Header value
*/
this.setRequestHeader = function(header, value) {
if (this.readyState != this.OPENED) {
throw "INVALID_STATE_ERR: setRequestHeader can only be called when state is OPEN";
}
if (!isAllowedHttpHeader(header)) {
console.warn('Refused to set unsafe header "' + header + '"');
return;
}
if (sendFlag) {
throw "INVALID_STATE_ERR: send flag is true";
}
headers[header] = value;
};
/**
* Gets a header from the server response.
*
* @param string header Name of header to get.
* @return string Text of the header or null if it doesn't exist.
*/
this.getResponseHeader = function(header) {
if (typeof header === "string" && this.readyState > this.OPENED && response.headers[header.toLowerCase()] && !errorFlag) {
return response.headers[header.toLowerCase()];
}
return null;
};
/**
* Gets all the response headers.
*
* @return string A string with all response headers separated by CR+LF
*/
this.getAllResponseHeaders = function() {
if (this.readyState < this.HEADERS_RECEIVED || errorFlag) {
return "";
}
var result = "";
for (var i in response.headers) {
// Cookie headers are excluded
if (i !== "set-cookie" && i !== "set-cookie2") {
result += i + ": " + response.headers[i] + "\r\n";
}
}
return result.substr(0, result.length - 2);
};
/**
* Gets a request header
*
* @param string name Name of header to get
* @return string Returns the request header or empty string if not set
*/
this.getRequestHeader = function(name) {
// @TODO Make this case insensitive
if (typeof name === "string" && headers[name]) {
return headers[name];
}
return "";
};
/**
* Sends the request to the server.
*
* @param string data Optional data to send as request body.
*/
this.send = function(data) {
if (this.readyState != this.OPENED) {
throw "INVALID_STATE_ERR: connection must be opened before send() is called";
}
if (sendFlag) {
throw "INVALID_STATE_ERR: send has already been called";
}
var ssl = false, local = false;
var url = Url.parse(settings.url);
var host, responseHandler, errorHandler;
// Determine the server
switch (url.protocol) {
case 'https:':
ssl = true;
host = url.hostname;
break;
case 'http:':
host = url.hostname;
break;
case 'file:':
local = true;
break;
case undefined:
case '':
host = "localhost";
break;
default:
throw "Protocol not supported.";
}
// Load files off the local filesystem (file://)
if (local) {
if (settings.method !== "GET") {
throw "XMLHttpRequest: Only GET method is supported";
}
if (settings.async) {
fs.readFile(url.pathname, 'utf8', function(error, data) {
if (error) {
self.handleError(error);
} else {
self.status = 200;
self.responseText = data;
self.responseXML = isXMLRequest() ? new XmlDOMParser().parseFromString(data) : null;
setState(self.DONE);
}
});
} else {
try {
this.responseText = fs.readFileSync(url.pathname, 'utf8');
self.responseXML = isXMLRequest() ? new XmlDOMParser().parseFromString(this.responseText) : null;
this.status = 200;
setState(self.DONE);
} catch(e) {
this.handleError(e);
}
}
return;
}
// Default to port 80. If accessing localhost on another port be sure
// to use http://localhost:port/path
var port = url.port || (ssl ? 443 : 80);
// Add query string if one is used
var uri = url.pathname + (url.search ? url.search : '');
// Set the Host header or the server may reject the request
headers.Host = host;
if (!((ssl && port === 443) || port === 80)) {
headers.Host += ':' + url.port;
}
// Set Basic Auth if necessary
if (settings.user) {
if (typeof settings.password == "undefined") {
settings.password = "";
}
var authBuf = new Buffer(settings.user + ":" + settings.password);
headers.Authorization = "Basic " + authBuf.toString("base64");
}
// Set content length header
if (settings.method === "GET" || settings.method === "HEAD") {
data = null;
} else if (data) {
headers["Content-Length"] = Buffer.isBuffer(data) ? data.length : Buffer.byteLength(data);
if (!headers["Content-Type"]) {
headers["Content-Type"] = "text/plain;charset=UTF-8";
}
} else if (settings.method === "POST") {
// For a post with no data set Content-Length: 0.
// This is required by buggy servers that don't meet the specs.
headers["Content-Length"] = 0;
}
var options = {
host: host,
port: port,
path: uri,
method: settings.method,
headers: headers,
agent: false
};
// Reset error flag
errorFlag = false;
// Handle async requests
if (settings.async) {
// Use the proper protocol
var doRequest = ssl ? https.request : http.request;
// Request is being sent, set send flag
sendFlag = true;
// As per spec, this is called here for historical reasons.
self.dispatchEvent("readystatechange");
// Handler for the response
responseHandler = function(resp) {
// Set response var to the response we got back
// This is so it remains accessable outside this scope
response = resp;
// Check for redirect
// @TODO Prevent looped redirects
if (response.statusCode === 302 || response.statusCode === 303 || response.statusCode === 307) {
// Change URL to the redirect location
settings.url = response.headers.location;
var url = Url.parse(settings.url);
// Set host var in case it's used later
host = url.hostname;
// Options for the new request
var newOptions = {
hostname: url.hostname,
port: url.port,
path: url.path,
method: response.statusCode === 303 ? 'GET' : settings.method,
headers: headers
};
// Issue the new request
request = doRequest(newOptions, responseHandler).on('error', errorHandler);
request.end();
// @TODO Check if an XHR event needs to be fired here
return;
}
response.setEncoding("utf8");
setState(self.HEADERS_RECEIVED);
self.status = response.statusCode;
response.on('data', function(chunk) {
// Make sure there's some data
if (chunk) {
self.responseText += chunk;
}
// Don't emit state changes if the connection has been aborted.
if (sendFlag) {
setState(self.LOADING);
}
});
response.on('end', function() {
if (sendFlag) {
self.responseXML = isXMLRequest() ? new XmlDOMParser().parseFromString(self.responseText) : null;
// Discard the 'end' event if the connection has been aborted
setState(self.DONE);
sendFlag = false;
}
});
response.on('error', function(error) {
self.handleError(error);
});
};
// Error handler for the request
errorHandler = function(error) {
self.handleError(error);
};
// Create the request
request = doRequest(options, responseHandler).on('error', errorHandler);
// Node 0.4 and later won't accept empty data. Make sure it's needed.
if (data) {
request.write(data);
}
request.end();
self.dispatchEvent("loadstart");
} else { // Synchronous
// Create a temporary file for communication with the other Node process
var contentFile = ".node-xmlhttprequest-content-" + process.pid;
var syncFile = ".node-xmlhttprequest-sync-" + process.pid;
fs.writeFileSync(syncFile, "", "utf8");
// The async request the other Node process executes
var execString = "var http = require('http'), https = require('https'), fs = require('fs');" +
"var doRequest = http" + (ssl ? "s" : "") + ".request;" +
"var options = " + JSON.stringify(options) + ";" +
"var responseText = '';" +
"var req = doRequest(options, function(response) {" +
"response.setEncoding('utf8');" +
"response.on('data', function(chunk) {" +
" responseText += chunk;" +
"});" +
"response.on('end', function() {" +
"fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-STATUS:' + response.statusCode + ',' + responseText, 'utf8');" +
"fs.unlinkSync('" + syncFile + "');" +
"});" +
"response.on('error', function(error) {" +
"fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');" +
"fs.unlinkSync('" + syncFile + "');" +
"});" +
"}).on('error', function(error) {" +
"fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');" +
"fs.unlinkSync('" + syncFile + "');" +
"});" +
(data ? "req.write('" + data.replace(/'/g, "\\'") + "');":"") +
"req.end();";
// Start the other Node Process, executing this string
var syncProc = spawn(process.argv[0], ["-e", execString]);
/*jshint noempty:true */
// Wait while the sync file is empty
while (fs.existsSync(syncFile)) {}
/*jshint noempty:false */
self.responseText = fs.readFileSync(contentFile, 'utf8');
self.responseXML = isXMLRequest() ? new XmlDOMParser().parseFromString(self.responseText) : null;
// Kill the child process once the file has data
syncProc.stdin.end();
// Remove the temporary file
fs.unlinkSync(contentFile);
if (self.responseText.match(/^NODE-XMLHTTPREQUEST-ERROR:/)) {
// If the file returned an error, handle it
var errorObj = self.responseText.replace(/^NODE-XMLHTTPREQUEST-ERROR:/, "");
self.handleError(errorObj);
} else {
// If the file returned okay, parse its data and move to the DONE state
self.status = self.responseText.replace(/^NODE-XMLHTTPREQUEST-STATUS:([0-9]*),.*/, "$1");
self.responseText = self.responseText.replace(/^NODE-XMLHTTPREQUEST-STATUS:[0-9]*,(.*)/, "$1");
setState(self.DONE);
}
}
};
/**
* Called when an error is encountered to deal with it.
*/
this.handleError = function(error) {
this.status = 503;
this.statusText = error;
this.responseText = error.stack;
errorFlag = true;
setState(this.DONE);
};
/**
* Aborts a request.
*/
this.abort = function() {
if (request) {
request.abort();
request = null;
}
headers = defaultHeaders;
this.responseText = "";
this.responseXML = "";
errorFlag = true;
if (this.readyState !== this.UNSENT && (this.readyState !== this.OPENED || sendFlag) && this.readyState !== this.DONE) {
sendFlag = false;
setState(this.DONE);
}
this.readyState = this.UNSENT;
};
/**
* Adds an event listener. Preferred method of binding to events.
*/
this.addEventListener = function(event, callback) {
if (!(event in listeners)) {
listeners[event] = [];
}
// Currently allows duplicate callbacks. Should it?
listeners[event].push(callback);
};
/**
* Remove an event callback that has already been bound.
* Only works on the matching funciton, cannot be a copy.
*/
this.removeEventListener = function(event, callback) {
if (event in listeners) {
// Filter will return a new array with the callback removed
listeners[event] = listeners[event].filter(function(ev) {
return ev !== callback;
});
}
};
/**
* Dispatch any events, including both "on" methods and events attached using addEventListener.
*/
this.dispatchEvent = function(event) {
if (typeof self["on" + event] === "function") {
self["on" + event]();
}
if (event in listeners) {
for (var i = 0, len = listeners[event].length; i < len; i++) {
listeners[event][i].call(self);
}
}
};
/**
* Changes readyState and calls onreadystatechange.
*
* @param int state New state
*/
var setState = function(state) {
if ((self.readyState !== state) || (settings.async && (self.readyState===self.LOADING))) {
self.readyState = state;
if (settings.async || self.readyState < self.OPENED || self.readyState === self.DONE) {
self.dispatchEvent("readystatechange");
}
if (settings.async && (self.readyState===self.LOADING)) {
self.dispatchEvent("progress");
}
if (self.readyState === self.DONE && !errorFlag) {
self.dispatchEvent("load");
// @TODO figure out InspectorInstrumentation::didLoadXHR(cookie)
self.dispatchEvent("loadend");
}
}
};
};
}).call(this,require('_process'),require("buffer").Buffer)
},{"_process":5966,"buffer":5955,"child_process":5954,"fs":5954,"http":5959,"https":5963,"url":5984,"xmldom":5930}],3476:[function(require,module,exports){
"use strict";
/**
* Emulation of browser `window` and `dom`. Just enough to make ITSA work.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module node-win
* @class window
* @static
*/
require('js-ext/lib/array.js');
var xmlhttprequest = require('./lib/XMLHttpRequest.js').XMLHttpRequest,
createHashMap = require('js-ext/extra/hashmap.js').createMap,
xmlDOMParser = require('xmldom').DOMParser,
Url = require('url'),
used = createHashMap(),
// vNodeParser = /(?:(^|#|\.)([^#\.\[\]]+))|(\[.+?\])/g,
count, doc, win, getHTML, reset, EventTypes;
EventTypes = createHashMap({
MouseEvents: function () {
this.initMouseEvent = function (type, bubbles, cancelable, view, detail,
screenX, screenY, clientX, clientY,
ctrlKey, altKey, shiftKey, metaKey,
button, relatedTarget) {
count('initMouseEvent');
this.ev = {
type:type,
bubbles:bubbles,
cancelable:cancelable,
view:view,
detail:detail,
screenX:screenX,
screenY:screenY,
clientX:clientX,
clientY:clientY,
ctrlKey:ctrlKey,
altKey:altKey,
shiftKey:shiftKey,
metaKey:metaKey,
button:button,
relatedTarget:relatedTarget
};
};
}
});
count = function (method) {
if (!used[method]) {
used[method] = 1;
} else {
used[method] += 1;
}
};
getHTML = function (node) {
var prop, val,
style, styles = [],
html = '';
if (!node.nodeName && node.nodeValue) {
// For text nodes, I return the uppercase text
// so that you can tell the parts generated at the server
// from the normal lowercase of the actual app when run on the client
return node.nodeValue.toUpperCase();
}
html += '<' + node.nodeName;
for (prop in node) {
val = node[prop];
// Ignore functions, those will be revived on the client side.
if (typeof val == 'function') continue;
switch (prop) {
case 'nodeName':
case 'parentNode':
case 'childNodes':
case 'pathname':
case 'search':
continue;
case 'checked':
if (val == 'false') continue;
break;
case 'href':
val = node.pathname;
break;
case 'className':
prop = 'class';
break;
case 'style':
if (val) {
for (style in val) {
if (val[style]) {
styles.push(style + ': ' + val[style]);
}
}
if (!styles.length) {
continue;
}
val = styles.join(';');
}
break;
}
html += ' ' + prop + '="' + val.replace('"', '\\"') + '"';
}
if (node.childNodes.length) {
html += '>' + node.childNodes.reduce(function (prev, node) {
return prev + getHTML(node);
}, '') + '</' + node.nodeName + '>';
}
else {
// I don't know why Mithril assigns the content of textareas
// to its value attribute instead of the innerHTML property.
// Since it doesn't have children, the closing tag has to be forced.
if (node.nodeName == 'TEXTAREA') {
html += '></TEXTAREA>';
} else {
html += '/>';
}
}
return html;
};
win = {
cancelAnimationFrame: function() {
},
console: require('polyfill/lib/window.console.js'),
CSSStyleDeclaration: {},
document: doc,
DOMParser: xmlDOMParser,
HTMLCollection: Array,
location: {},
navigator: {
userAgent: 'fake',
stats: {
clear: function () {
used = {};
},
get: function () {
return used;
}
},
reset: reset,
getHTML: function () {
return getHTML(doc.body);
},
navigate: function (url) {
var u = Url.parse(url, false, true);
win.location.search = u.search || '';
win.location.pathname = u.pathname || '';
win.location.hash = u.hash || '';
}
},
NodeList: Array,
performance: function () {
var timestamp = 50;
this.$elapse = function(amount) {
timestamp += amount;
};
this.now = function() {
return timestamp;
};
},
requestAnimationFrame: function(callback) {
var instance = this;
instance.requestAnimationFrame.$callback = callback;
instance.requestAnimationFrame.$resolve = function() {
instance.requestAnimationFrame.$callback && instance.requestAnimationFrame.$callback();
instance.requestAnimationFrame.$callback = null;
instance.performance.$elapse(20);
};
},
XMLHttpRequest: xmlhttprequest
};
reset = function () {
var body = doc.createElement('body');
win.location.search = "?/";
win.location.pathname = "/";
win.location.hash = "";
win.history = {};
win.history.pushState = function(data, title, url) {
win.location.pathname = win.location.search = win.location.hash = url;
},
win.history.replaceState = function(data, title, url) {
win.location.pathname = win.location.search = win.location.hash = url;
};
doc.appendChild(body);
doc.body = body;
};
reset();
module.exports = win;
},{"./lib/XMLHttpRequest.js":3475,"js-ext/extra/hashmap.js":3477,"js-ext/lib/array.js":3478,"polyfill/lib/window.console.js":3482,"url":5984,"xmldom":5930}],3477:[function(require,module,exports){
module.exports=require(4)
},{}],3478:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":3481}],3479:[function(require,module,exports){
module.exports=require(14)
},{}],3480:[function(require,module,exports){
module.exports=require(15)
},{}],3481:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3479,"./lib/window.console.js":3480}],3482:[function(require,module,exports){
module.exports=require(15)
},{}],3483:[function(require,module,exports){
module.exports=require(264)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],3484:[function(require,module,exports){
module.exports=require(265)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],3485:[function(require,module,exports){
arguments[4][266][0].apply(exports,arguments)
},{"./css/drag.css":3484,"event-dom":3486,"js-ext":3582,"js-ext/extra/hashmap.js":3581,"node-plugin":3598,"polyfill":3770,"useragent":3786,"vdom":3839,"window-ext":3840}],3486:[function(require,module,exports){
"use strict";
/**
* Integrates DOM-events to event. more about DOM-events:
* http://www.smashingmagazine.com/2013/11/12/an-introduction-to-dom-events/
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @example
* Event = require('event-dom')(window);
*
* @module event
* @submodule event-dom
* @class Event
* @since 0.0.1
*/
var NAME = '[event-dom]: ',
Event = require('event'),
later = require('utils').later,
createHashMap = require('js-ext/extra/hashmap.js').createMap,
OUTSIDE = 'outside',
REGEXP_NODE_ID = /^#\S+$/,
REGEXP_EXTRACT_NODE_ID = /#(\S+)/,
REGEXP_UI_OUTSIDE = /^.+outside$/,
TIME_BTN_PRESSED = 200,
PURE_BUTTON_ACTIVE = 'pure-button-active',
UI = 'UI:',
NODE = 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
TAP = 'tap',
CLICK = 'click',
RIGHTCLICK = 'right'+CLICK,
CENTERCLICK = 'center'+CLICK,
EV_REMOVED = UI+NODE+REMOVE,
EV_INSERTED = UI+NODE+INSERT,
EV_CONTENT_CHANGE = UI+NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = UI+ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = UI+ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = UI+ATTRIBUTE+INSERT,
mutationEventsDefined = false,
NO_DEEP_SEARCH = {},
/*
* Internal hash containing all DOM-events that are listened for (at `document`).
*
* DOMEvents = {
* 'tap': callbackFn,
* 'mousemove': callbackFn,
* 'keypress': callbackFn
* }
*
* @property DOMEvents
* @default {}
* @type Object
* @private
* @since 0.0.1
*/
DOMEvents = {};
require('js-ext/lib/string.js');
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('polyfill/polyfill-base.js');
module.exports = function (window) {
var DOCUMENT = window.document,
_domSelToFunc, _evCallback, _findCurrentTargets, _preProcessor, _setupEvents, _setupMutationListener, _teardownMutationListener,
_setupDomListener, _teardownDomListener, SORT, _sortFunc, _sortFuncReversed, _getSubscribers, _selToFunc, MUTATION_EVENTS, preventClick;
require('vdom')(window);
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.EventDom) {
return Event; // Event was already extended
}
console.warn('event-dom 16');
MUTATION_EVENTS = [EV_REMOVED, EV_INSERTED, EV_CONTENT_CHANGE, EV_ATTRIBUTE_REMOVED, , EV_ATTRIBUTE_CHANGED, EV_ATTRIBUTE_INSERTED];
/*
* Transforms the selector to a valid function
*
* @method _selToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_selToFunc = function(customEvent, subscriber) {
Event._sellist.some(function(selFn) {
return selFn(customEvent, subscriber);
});
};
/*
* Creates a filterfunction out of a css-selector. To be used for catching any dom-element, without restrictions
* of any context (like Parcels can --> Parcel.Event uses _parcelSelToDom instead)
* On "non-outside" events, subscriber.t is set to the node that first matches the selector
* so it can be used to set as e.target in the final subscriber
*
* @method _domSelToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_domSelToFunc = function(customEvent, subscriber) {
// this stage is runned during subscription
var outsideEvent = REGEXP_UI_OUTSIDE.test(customEvent),
selector = subscriber.f,
context = subscriber.o,
vnode = subscriber.o.vnode,
isCustomElement = vnode && vnode.isItag,
visibleContent = isCustomElement && !vnode.domNode.contentHidden,
nodeid, byExactId, newTarget, deepSearch;
isCustomElement = false;
console.log(NAME, '_domSelToFunc type of selector = '+typeof selector);
// note: selector could still be a function: in case another subscriber
// already changed it.
if ((!selector && !isCustomElement) || (typeof selector === 'function')) {
subscriber.n || (subscriber.n = isCustomElement ? context : DOCUMENT);
return true;
}
selector || (selector='');
nodeid = selector.match(REGEXP_EXTRACT_NODE_ID);
nodeid ? (subscriber.nId=nodeid[1]) : (subscriber.n=isCustomElement ? context : DOCUMENT);
byExactId = REGEXP_NODE_ID.test(selector);
deepSearch = !NO_DEEP_SEARCH[customEvent];
// set the selector to `subscriber._s` so that e.currentTarget can calculate it:
subscriber._s = selector;
subscriber.f = function(e) {
// this stage is runned when the event happens
console.log(NAME, '_domSelToFunc inside filter. selector: '+selector);
var node = e.target,
vnode = node.vnode,
character1 = selector && selector.substr(1),
match = false;
if (!isCustomElement || visibleContent || context.contains(node)) {
if (selector==='') {
match = true;
}
else {
// e.target is the most deeply node in the dom-tree that caught the event
// our listener uses `selector` which might be a node higher up the tree.
// we will reset e.target to this node (if there is a match)
// note that e.currentTarget will always be `document` --> we're not interested in that
// also, we don't check for `node`, but for node.matchesSelector: the highest level `document`
// is not null, yet it doesn't have .matchesSelector so it would fail
// we DON'T want this for the focus and blur event!
if (deepSearch) {
if (vnode) {
// we go through the vdom
if (!vnode.removedFromDOM) {
while (vnode && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = vnode.domNode;
}
vnode = vnode.vParent;
}
}
}
else {
// we go through the dom
while (node.matchesSelector && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the dom');
match = byExactId ? (node.id===character1) : node.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = node;
}
node = node.parentNode;
}
}
}
else {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
if (vnode) {
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
}
else {
match = node.matchesSelector && (byExactId ? (node.id===character1) : node.matchesSelector(selector));
}
match && (e.sourceTarget=node);
}
}
}
if (outsideEvent && !match) {
// there is a match for the outside-event:
// we need to set e.sourceTarget and e.target:
newTarget = DOCUMENT.getElement(selector, true);
if (newTarget) {
e.sourceTarget = node;
subscriber.t = newTarget;
}
else {
// make return `false` because the selector is not in the dom
match = true;
}
}
console.log(NAME, '_domSelToFunc filter returns '+(!outsideEvent ? match : !match));
return !outsideEvent ? match : !match;
};
return true;
};
// at this point, we need to find out what are the current node-refs. whenever there is
// a filter that starts with `#` --> in those cases we have a bubble-chain, because the selector isn't
// set up with `document` at its root.
// we couldn't do this at time of subscribtion, for the nodes might not be there at that time.
// however, we only need to do this once: we store the value if we find them
// no problem when the nodes leave the dom later: the previous filter wouldn't pass
_findCurrentTargets = function(subscribers) {
console.log(NAME, '_findCurrentTargets');
subscribers.forEach(
function(subscriber) {
console.log(NAME, '_findCurrentTargets for single subscriber. nId: '+subscriber.nId);
subscriber.nId && (subscriber.n=DOCUMENT.getElementById(subscriber.nId, true));
}
);
};
/*
* Generates an event through our Event-system. Does the actual transportation from DOM-events
* into our Eventsystem. It also looks at the response of our Eventsystem: if our system
* halts or preventDefaults the customEvent, then the original DOM-event will be preventDefaulted.
*
* @method _evCallback
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_evCallback = function(e) {
console.log(NAME, '_evCallback');
var allSubscribers = Event._subs,
eType = e.type,
eventobject, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs, subsOutside,
subscribers, eventobjectOutside, wildcard_named_subsOutside, customEvent, eventName, which;
eventName = eType;
// first: a `click` event might be needed to transformed into `rightclick`:
if (eventName===CLICK) {
which = e.which;
(which===2) && (eventName=CENTERCLICK);
(which===3) && (eventName=RIGHTCLICK);
}
if (eventName==='tap') {
// prevent the next click-event
preventClick = true;
e.clientX || (e.clientX = e.center && e.center.x);
e.clientY || (e.clientY = e.center && e.center.y);
}
else if (preventClick && (eventName===CLICK)) {
preventClick = false;
return;
}
if (eventName===CLICK) {
eventName = 'tap';
e.center = {
x: e.clientX,
y: e.clientY
};
e.eventType = 4;
e.pointerType = 'mouse';
e.tapCount = 1;
}
customEvent = 'UI:'+eventName;
subs = allSubscribers[customEvent];
wildcard_named_subs = allSubscribers['*:'+eventName];
named_wildcard_subs = allSubscribers['UI:*'];
wildcard_wildcard_subs = allSubscribers['*:*'];
// Emit the dom-event though our eventsystem:
// NOTE: emit() needs to be synchronous! otherwise we wouldn't be able
// to preventDefault in time
//
// e = eventobject from the DOM-event OR gesture-event
// eventobject = eventobject from our Eventsystem, which get returned by calling `emit()`
// now so the work:
subscribers = _getSubscribers(e, true, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
eventobject = Event._emit(e.target, customEvent, e, subscribers, [], _preProcessor, false, true);
// now check outside subscribers
subsOutside = allSubscribers[customEvent+OUTSIDE];
wildcard_named_subsOutside = allSubscribers['*:'+eventName+OUTSIDE];
subscribers = _getSubscribers(e, true, subsOutside, wildcard_named_subsOutside);
eventobjectOutside = Event._emit(e.target, customEvent+OUTSIDE, e, subscribers, [], _preProcessor, false, true);
// if eventobject was preventdefaulted or halted: take appropriate action on
// the original dom-event. Note: only the original event can caused this, not the outsideevent
// stopPropagation on the original eventobject has no impact on our eventsystem, but who know who else is watching...
// be carefull though: not all gesture events have e.stopPropagation
eventobject.status.halted && e.stopPropagation && e.stopPropagation();
// now we might need to preventDefault the original event.
// be carefull though: not all gesture events have e.preventDefault
if ((eventobject.status.halted || eventobject.status.defaultPrevented || eventobject.status.defaultPreventedContinue) && e.preventDefault) {
e.preventDefault();
}
if (eventobject.status.ok) {
// last step: invoke the aftersubscribers
// we need to do this asynchronous: this way we pass them AFTER the DOM-event's defaultFn
// also make sure to paas-in the payload of the manipulated eventobject
subscribers = _getSubscribers(e, false, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent, eventobject, [], subscribers, _preProcessor, true), 10);
// now check outside subscribers
subscribers = _getSubscribers(e, false, subsOutside, wildcard_named_subsOutside);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent+OUTSIDE, eventobjectOutside, [], subscribers, _preProcessor, true), 10);
}
};
/*
* Creates an array of subscribers in the right order, conform their position in the DOM.
* Only subscribers that match the filter are involved.
*
* @method _getSubscribers
* @param e {Object} eventobject
* @param before {Boolean} whether it is a before- or after-subscriber
* @param subs {Array} array with subscribers
* @param wildcard_named_subs {Array} array with subscribers
* @param named_wildcard_subs {Array} array with subscribers
* @param wildcard_wildcard_subs {Array} array with subscribers
* @private
* @since 0.0.1
*/
_getSubscribers = function(e, before, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs) {
var subscribers = [],
beforeOrAfter = before ? 'b' : 'a',
saveConcat = function(extrasubs) {
extrasubs && extrasubs[beforeOrAfter] && (subscribers=subscribers.concat(extrasubs[beforeOrAfter]));
};
saveConcat(subs);
saveConcat(wildcard_named_subs);
saveConcat(named_wildcard_subs);
saveConcat(wildcard_wildcard_subs);
if (subscribers.length>0) {
subscribers = function(array, testFunc) {
// quickest way to filter an array: see http://jsperf.com/array-filter-performance/4
var filtered = array.slice(0), i;
for (i=array.length-1; i>=0; i--) {
console.log(NAME, 'filtercheck for subscriber');
testFunc(array[i]) || filtered.splice(i, 1);
}
return filtered;
}(subscribers, function(subscriber) {return (!subscriber.f || subscriber.f.call(subscriber.o, e));});
if (subscribers.length>0) {
// _findCurrentTargets(subscribers);
// sorting, based upon the sortFn
subscribers.sort(SORT);
}
}
return subscribers;
};
/*
* Sets e.target and e.sourceTarget for the single subscriber.
* Needs to be done for evenry single subscriber, because with a single event, these values change for each subscriber
*
* @method _preProcessor
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_preProcessor = function(subscriber, e) {
console.log(NAME, '_preProcessor');
// inside the aftersubscribers, we may need exit right away.
// this would be the case whenever stopPropagation or stopImmediatePropagation was called
// in case the subscribernode equals the node on which stopImmediatePropagation was called: return true
var propagationStopped, immediatePropagationStopped,
targetnode = (subscriber.t || subscriber.n);
immediatePropagationStopped = e.status.immediatePropagationStopped;
if (immediatePropagationStopped && ((immediatePropagationStopped===targetnode) || !immediatePropagationStopped.contains(targetnode))) {
console.log(NAME, '_preProcessor will return true because of immediatePropagationStopped');
return true;
}
// in case the subscribernode does not fall within or equals the node on which stopPropagation was called: return true
propagationStopped = e.status.propagationStopped;
if (propagationStopped && (propagationStopped!==targetnode) && !propagationStopped.contains(targetnode)) {
console.log(NAME, '_preProcessor will return true because of propagationStopped');
return true;
}
e._s = subscriber._s;
// now we might need to set e.target to the right node:
// the filterfunction might have found the true domnode that should act as e.target
// and set it at subscriber.t
// also, we need to backup the original e.target: this one should be reset when
// we encounter a subscriber with its own filterfunction instead of selector
if (subscriber.t) {
e.sourceTarget || (e.sourceTarget=e.target);
e.target = subscriber.t;
}
else {
e.sourceTarget && (e.target=e.sourceTarget);
}
return false;
};
/*
* Transports DOM-events to the Event-system. Catches events at their most early stage:
* their capture-phase. When these events happen, a new customEvent is generated by our own
* Eventsystem, by calling _evCallback(). This way we keep DOM-events and our Eventsystem completely separated.
*
* @method _setupDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_setupDomListener = function(customEvent, subscriber) {
console.log(NAME, '_setupDomListener');
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0],
eventName = eventSplitted[1],
outsideEvent = REGEXP_UI_OUTSIDE.test(eventName);
// be careful: anyone could also register an `outside`-event.
// in those cases, the DOM-listener must be set up without `outside`
outsideEvent && (eventName=eventName.substring(0, eventName.length-7));
// if eventName equals `mouseover` or `mouseleave` then we quit:
// people should use `mouseover` and `mouseout`
if ((eventName==='mouseenter') || (eventName==='mouseleave')) {
console.warn(NAME, 'Subscription to '+eventName+' not supported, use mouseover and mouseout: this eventsystem uses these non-noisy so they act as mouseenter and mouseleave');
return;
}
// only accept tap-events, yet later on we WILL need to listen for click-events
(eventName===CLICK) && (eventName=TAP);
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(emitterName+':'+eventName+(outsideEvent ? OUTSIDE : ''), subscriber);
// already registered? then return, also return if someone registered for UI:*
if (DOMEvents[eventName] || (eventName==='*')) {
// cautious: one might have registered the event, but not yet the outsideevent.
// in that case: save this setting:
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
return;
}
DOMEvents[eventName] = true;
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
// one exception: windowresize should listen to the window-object
if (eventName==='resize') {
window.addEventListener(eventName, _evCallback);
}
else {
((eventName===RIGHTCLICK) || (eventName===CENTERCLICK)) && (eventName=TAP);
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.addEventListener(eventName, _evCallback, true);
// listen for both `tap` and `click` events to happen
(eventName===TAP) && DOCUMENT.addEventListener(CLICK, _evCallback, true);
}
};
_setupEvents = function() {
var lastFocussed;
// make sure disabled buttons don't work:
Event.before(['tap', 'press'], function(e) {
e.preventDefault();
}, '.pure-button-disabled, button[disabled]');
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.before(
'keydown',
function(e) {
e._buttonPressed = true;
Event.emit(e.target, 'UI:tap', e);
},
function(e) {
var keyCode = e.keyCode;
return (e.target.getTagName()==='BUTTON') && ((keyCode===13) || (keyCode===32));
}
);
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.after(
'tap',
function(e) {
var buttonNode = e.target;
if (e._buttonPressed) {
buttonNode.setClass(PURE_BUTTON_ACTIVE);
e._noRender = true;
// even if the node isn't in the DOM, we can still try to manipulate it:
// the vdom makes sure no errors occur when the node is already removed
later(buttonNode.removeClass.bind(buttonNode, PURE_BUTTON_ACTIVE), TIME_BTN_PRESSED);
}
}
);
// fix activeElement on Mac
Event.before('focus', function(e) {
// will come here more often because of bubblechain
// however, the last pass-through will set the deepest node
lastFocussed = e.target;
});
// patching DOCUMENT.activeElement because it doesn't work well in a Mac: https://developer.mozilla.org/en-US/docs/Web/API/document.activeElement
// DOCUMENT._activeElement is used with the patch for DOCUMENT.activeElement its getter
Event.after('focus', function() {
DOCUMENT._activeElement = lastFocussed;
});
Event.before('blur', function() {
DOCUMENT._activeElement = null;
});
// Note: window.document has no prototype
Object.defineProperty(DOCUMENT, 'activeElement', {
get: function() {
return DOCUMENT._activeElement || DOCUMENT.body;
}
});
};
_setupMutationListener = function() {
DOCUMENT.hasMutationSubs = true;
if (!mutationEventsDefined) {
Event.defineEvent(EV_REMOVED).unPreventable();
Event.defineEvent(EV_INSERTED).unPreventable();
Event.defineEvent(EV_CONTENT_CHANGE).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_REMOVED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_CHANGED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_INSERTED).unPreventable();
mutationEventsDefined = true;
}
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFunc = function(subscriberOne, subscriberTwo) {
return (subscriberTwo.t || subscriberTwo.n).contains(subscriberOne.t || subscriberOne.n) ? -1 : 1;
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFuncReversed = function(subscriberOne, subscriberTwo) {
return (subscriberOne.t || subscriberOne.n).contains(subscriberTwo.t || subscriberTwo.n) ? 1 : -1;
};
/*
* Removes DOM-eventsubscribers from document when they are no longer needed.
*
* @method _teardownDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @private
* @since 0.0.2
*/
_teardownDomListener = function(customEvent) {
var customEventWithoutOutside = customEvent.endsWith(OUTSIDE) ? customEvent.substr(0, customEvent.length-7) : customEvent,
eventSplitted = customEventWithoutOutside.split(':'),
eventName = eventSplitted[1],
stillInUse;
if ((customEventWithoutOutside===CLICK) || (customEventWithoutOutside===RIGHTCLICK) || (customEventWithoutOutside===CENTERCLICK)) {
stillInUse = Event._subs[CLICK] ||
Event._subs[CLICK+OUTSIDE];
Event._subs[RIGHTCLICK] ||
Event._subs[RIGHTCLICK+OUTSIDE],
Event._subs[CENTERCLICK] ||
Event._subs[CENTERCLICK+OUTSIDE];
eventName = CLICK;
}
else {
stillInUse = Event._subs[customEventWithoutOutside] || Event._subs[customEventWithoutOutside+OUTSIDE];
}
if (!stillInUse) {
console.log(NAME, '_teardownDomListener '+customEvent);
// remove eventlistener from `document`
// one exeption: windowresize should listen to the window-object
if (eventName==='resize') {
window.removeEventListener(eventName, _evCallback);
}
else {
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.removeEventListener(eventName, _evCallback, true);
}
delete DOMEvents[eventName];
}
};
_teardownMutationListener = function() {
if (!Event._subs[EV_REMOVED] &&
!Event._subs[EV_INSERTED] &&
!Event._subs[EV_CONTENT_CHANGE] &&
!Event._subs[EV_ATTRIBUTE_REMOVED] &&
!Event._subs[EV_ATTRIBUTE_CHANGED] &&
!Event._subs[EV_ATTRIBUTE_INSERTED]
) {
DOCUMENT.hasMutationSubs = false;
}
};
// Now a very tricky one:
// Some browsers do an array.sort down-top instead of top-down.
// In those cases we need another sortFn, for the position on an equal match should fall
// behind instead of before (which is the case on top-down sort)
[1,2].sort(function(a /*, b */) {
SORT || (SORT=(a===2) ? _sortFuncReversed : _sortFunc);
});
// Now we do some initialization in order to make DOM-events work:
Object.defineProperty(Event._defaultEventObj, 'currentTarget', {
get: function() {
var ev = this,
e_target = ev.target;
if (e_target && ev._s) {
if (e_target.matches(ev._s)) {
return e_target;
}
return e_target.inside(ev._s) || undefined;
}
}
});
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(UI+'*', _setupDomListener, Event)
._setEventObjProperty('stopPropagation', function() {this.status.ok || (this.status.propagationStopped = this.target);})
._setEventObjProperty('stopImmediatePropagation', function() {this.status.ok || (this.status.immediatePropagationStopped = this.target);});
// Notify when someone subscribes to any event at all --> we might need to transform the filterFn from a selector into a true fnction
// this is already done automaticly by _setupDomListener fo UI:* events
Event.notify('*:*', function(customEvent, subscriber) {
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0];
if ((emitterName!=='UI') && (typeof subscriber.f==='string')) {
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(customEvent, subscriber);
}
}, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(UI+'*', _teardownDomListener, Event);
Event._sellist = [_domSelToFunc];
_setupEvents();
// making HTMLElement to be able to emit using event-emitter:
(function(HTMLElementPrototype) {
HTMLElementPrototype.merge(Event.Emitter('UI'));
}(window.HTMLElement.prototype));
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(MUTATION_EVENTS, _setupMutationListener, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(MUTATION_EVENTS, _teardownMutationListener, Event);
// Note: window.document has no prototype
DOCUMENT.suppressMutationEvents = function(suppress) {
this._suppressMutationEvents = suppress;
};
// Event.noDeepDomEvt and Event._domCallback are the only method that is added to Event.
// We need to do this, because `event-mobile` needs access to the same method.
// We could have done without this method and instead listen for a custom-event to handle
// Mobile events, however, that would lead into 2 eventcycli which isn't performant.
/**
*
* @method noDeepDomEvt
* @param domEvent {String} the eventName that should be processed without deepsearch
* @param e {Object} eventobject
* @for Event
* @chainable
* @since 0.0.1
*/
Event.noDeepDomEvt = function(domEvent) {
domEvent.contains(':') || (domEvent=UI+domEvent);
NO_DEEP_SEARCH[domEvent] = true;
return this;
};
/**
* Does the actual transportation from DOM-events into the Eventsystem. It also looks at the response of
* the Eventsystem: on e.halt() or e.preventDefault(), the original DOM-event will be preventDefaulted.
*
* @method _domCallback
* @param eventName {String} the customEvent that is transported to the eventsystem
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
Event._domCallback = function(e) {
_evCallback(e);
};
// store module:
window._ITSAmodules.EventDom = Event;
return Event;
};
},{"event":3490,"js-ext/extra/hashmap.js":3506,"js-ext/lib/array.js":3507,"js-ext/lib/object.js":3508,"js-ext/lib/string.js":3509,"polyfill/polyfill-base.js":3521,"utils":3522,"vdom":3580}],3487:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":3492,"js-ext/lib/object.js":3493,"polyfill/polyfill-base.js":3505}],3488:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":3487}],3489:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":3487,"js-ext/extra/classes.js":3491,"js-ext/lib/object.js":3493}],3490:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":3487,"./event-emitter.js":3488,"./event-listener.js":3489}],3491:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":3493,"js-ext/extra/hashmap.js":3492,"polyfill/polyfill-base.js":3496}],3492:[function(require,module,exports){
module.exports=require(4)
},{}],3493:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3492,"polyfill/polyfill-base.js":3496,"utils":3497}],3494:[function(require,module,exports){
module.exports=require(14)
},{}],3495:[function(require,module,exports){
module.exports=require(15)
},{}],3496:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3494,"./lib/window.console.js":3495}],3497:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3498,"./lib/timers.js":3499}],3498:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3492,"polyfill/polyfill-base.js":3502}],3499:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3502}],3500:[function(require,module,exports){
module.exports=require(14)
},{}],3501:[function(require,module,exports){
module.exports=require(15)
},{}],3502:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3500,"./lib/window.console.js":3501}],3503:[function(require,module,exports){
module.exports=require(14)
},{}],3504:[function(require,module,exports){
module.exports=require(15)
},{}],3505:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3503,"./lib/window.console.js":3504}],3506:[function(require,module,exports){
module.exports=require(4)
},{}],3507:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":3512}],3508:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3506,"polyfill/polyfill-base.js":3512,"utils":3513}],3509:[function(require,module,exports){
module.exports=require(29)
},{}],3510:[function(require,module,exports){
module.exports=require(14)
},{}],3511:[function(require,module,exports){
module.exports=require(15)
},{}],3512:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3510,"./lib/window.console.js":3511}],3513:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3514,"./lib/timers.js":3515}],3514:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3506,"polyfill/polyfill-base.js":3518}],3515:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3518}],3516:[function(require,module,exports){
module.exports=require(14)
},{}],3517:[function(require,module,exports){
module.exports=require(15)
},{}],3518:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3516,"./lib/window.console.js":3517}],3519:[function(require,module,exports){
module.exports=require(14)
},{}],3520:[function(require,module,exports){
module.exports=require(15)
},{}],3521:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3519,"./lib/window.console.js":3520}],3522:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3523,"./lib/timers.js":3524}],3523:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3506,"polyfill/polyfill-base.js":3527}],3524:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3527}],3525:[function(require,module,exports){
module.exports=require(14)
},{}],3526:[function(require,module,exports){
module.exports=require(15)
},{}],3527:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3525,"./lib/window.console.js":3526}],3528:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],3529:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":3533,"js-ext/extra/hashmap.js":3530,"polyfill/polyfill-base.js":3539}],3530:[function(require,module,exports){
module.exports=require(4)
},{}],3531:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":3532,"../lib/object.js":3533,"./classes.js":3529,"js-ext/extra/hashmap.js":3530,"polyfill/lib/weakmap.js":3537}],3532:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":3539}],3533:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3530,"polyfill/polyfill-base.js":3539,"utils":3540}],3534:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":3539}],3535:[function(require,module,exports){
module.exports=require(29)
},{}],3536:[function(require,module,exports){
module.exports=require(14)
},{}],3537:[function(require,module,exports){
module.exports=require(57)
},{}],3538:[function(require,module,exports){
module.exports=require(15)
},{}],3539:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3536,"./lib/window.console.js":3538}],3540:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3541,"./lib/timers.js":3542}],3541:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3530,"polyfill/polyfill-base.js":3545}],3542:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3545}],3543:[function(require,module,exports){
module.exports=require(14)
},{}],3544:[function(require,module,exports){
module.exports=require(15)
},{}],3545:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3543,"./lib/window.console.js":3544}],3546:[function(require,module,exports){
module.exports=require(66)
},{}],3547:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":3546}],3548:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":3546}],3549:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":3546}],3550:[function(require,module,exports){
module.exports=require(14)
},{}],3551:[function(require,module,exports){
module.exports=require(15)
},{}],3552:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3550,"./lib/window.console.js":3551}],3553:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3554,"./lib/timers.js":3555}],3554:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3530,"polyfill/polyfill-base.js":3558}],3555:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3558}],3556:[function(require,module,exports){
module.exports=require(14)
},{}],3557:[function(require,module,exports){
module.exports=require(15)
},{}],3558:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3556,"./lib/window.console.js":3557}],3559:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":3560}],3560:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":3561,"js-ext/lib/object.js":3562}],3561:[function(require,module,exports){
module.exports=require(4)
},{}],3562:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3561,"polyfill/polyfill-base.js":3565,"utils":3566}],3563:[function(require,module,exports){
module.exports=require(14)
},{}],3564:[function(require,module,exports){
module.exports=require(15)
},{}],3565:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3563,"./lib/window.console.js":3564}],3566:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3567,"./lib/timers.js":3568}],3567:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3561,"polyfill/polyfill-base.js":3571}],3568:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3571}],3569:[function(require,module,exports){
module.exports=require(14)
},{}],3570:[function(require,module,exports){
module.exports=require(15)
},{}],3571:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3569,"./lib/window.console.js":3570}],3572:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":3530,"js-ext/lib/object.js":3533,"js-ext/lib/string.js":3535,"polyfill":3552,"polyfill/extra/transition.js":3547,"polyfill/extra/vendorCSS.js":3549}],3573:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":3530,"js-ext/lib/object.js":3533,"polyfill":3552}],3574:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":3530,"js-ext/lib/object.js":3533,"js-ext/lib/string.js":3535,"polyfill":3552}],3575:[function(require,module,exports){
arguments[4][95][0].apply(exports,arguments)
},{"../css/element.css":3528,"./attribute-extractor.js":3572,"./element-array.js":3573,"./html-parser.js":3576,"./node-parser.js":3577,"./vdom-ns.js":3578,"./vnode.js":3579,"js-ext/extra/hashmap.js":3530,"js-ext/lib/object.js":3533,"js-ext/lib/promise.js":3534,"js-ext/lib/string.js":3535,"polyfill":3552,"polyfill/extra/transition.js":3547,"polyfill/extra/transitionend.js":3548,"polyfill/extra/vendorCSS.js":3549,"utils":3553,"window-ext":3559}],3576:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":3572,"./vdom-ns.js":3578,"js-ext/extra/hashmap.js":3530,"js-ext/lib/object.js":3533,"polyfill":3552}],3577:[function(require,module,exports){
arguments[4][97][0].apply(exports,arguments)
},{"./attribute-extractor.js":3572,"./vdom-ns.js":3578,"./vnode.js":3579,"js-ext/extra/hashmap.js":3530,"js-ext/lib/object.js":3533,"polyfill":3552}],3578:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":3530,"js-ext/lib/object.js":3533,"polyfill":3552}],3579:[function(require,module,exports){
"use strict";
/**
* Delivers the `vnode` prototype object, which is a virtualisation of an `Element` inside the Dom.
* These Elements work smoothless with the vdom (see ...).
*
* vnodes are much quicker to access and walk through than native dom-nodes. However, this is a module you don't need
* by itself: `Element`-types use these features under the hood.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* <br>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
*
* @module vdom
* @submodule vnode
* @class vnode
* @since 0.0.1
*/
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('js-ext/lib/string.js');
require('polyfill');
var createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.VNode) {
return window._ITSAmodules.VNode; // VNODE was already created
}
console.warn('VDOM 12');
var NS = require('./vdom-ns.js')(window),
extractor = require('./attribute-extractor.js')(window),
DOCUMENT = window.document,
LightMap = require('js-ext/extra/lightmap.js'),
MUTATION_EVENTS = new LightMap(),
BATCH_WILL_RUN = false,
xmlNS = NS.xmlNS,
nodeids = NS.nodeids,
htmlToVNodes = require('./html-parser.js')(window),
timers = require('utils/lib/timers.js'),
async = timers.async,
later = timers.later,
/*jshint proto:true */
PROTO_SUPPORTED = !!Object.__proto__,
/*jshint proto:false */
// cleanup memory after 1 minute: removed nodes SHOULD NOT be accessed afterwards
// because vnode would be recalculated and might be different from before
DESTROY_DELAY = 60000,
unescapeEntities = NS.UnescapeEntities,
NTH_CHILD_REGEXP = /^(?:(\d*)[n|N])([\+|\-](\d+))?$/, // an+b
STRING = 'string',
CLASS = 'class',
STYLE = 'style',
ID = 'id',
NODE= 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
EV_REMOVED = NODE+REMOVE,
EV_INSERTED = NODE+INSERT,
EV_CONTENT_CHANGE = NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = ATTRIBUTE+INSERT,
SPLIT_CHARACTER = createHashMap({
' ': true,
'>': true,
'+': true, // only select the element when it is immediately preceded by the former element
'~': true // only the element when it has the former element as a sibling. (just like `+`, but less strict)
}),
STORABLE_SPLIT_CHARACTER = createHashMap({
'>': true,
'+': true,
'~': true
}),
SIBLING_MATCH_CHARACTER = createHashMap({
'+': true,
'~': true
}),
ATTR_DETAIL_SPECIFIERS = createHashMap({
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to attribute-name end-tokens.
*
* @property END_ATTRIBUTENAME
* @default {
* '=': true,
* ']': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
END_ATTRIBUTENAME = createHashMap({
'=': true,
']': true,
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to different changes of Element nodeType changes.
*
* @property NODESWITCH
* @default {
* 1: {
* 1: 1,
* 3: 2,
* 8: 3
* },
* 3: {
* 1: 4,
* 3: 5,
* 8: 6
* },
* 8: {
* 1: 7,
* 3: 8,
* 8: 9
* }
* }
* @type Object
* @protected
* @since 0.0.1
*/
NODESWITCH = createHashMap({
1: createHashMap({
1: 1, // oldNodeType==Element, newNodeType==Element
3: 2, // oldNodeType==Element, newNodeType==TextNode
8: 3 // oldNodeType==Element, newNodeType==Comment
}),
3: createHashMap({
1: 4, // oldNodeType==TextNode, newNodeType==Element
3: 5, // oldNodeType==TextNode, newNodeType==TextNode
8: 6 // oldNodeType==TextNode, newNodeType==Comment
}),
8: createHashMap({
1: 7, // oldNodeType==Comment, newNodeType==Element
3: 8, // oldNodeType==Comment, newNodeType==TextNode
8: 9 // oldNodeType==Comment, newNodeType==Comment
})
}),
/**
* Object to gain quick access to selector start-tokens.
*
* @property SELECTOR_IDENTIFIERS
* @default {
* '#': 1,
* '.': 2,
* '[': 3
* }
* @type Object
* @protected
* @since 0.0.1
*/
SELECTOR_IDENTIFIERS = createHashMap({
'#': 1,
'.': 2,
'[': 3,
':': 4
}),
PSEUDO_FIRST_CHILD = ':first-child',
PSEUDO_FIRST_OF_TYPE = ':first-of-type',
PSEUDO_LAST_CHILD = ':last-child',
PSEUDO_LAST_OF_TYPE = ':last-of-type',
PSEUDO_NTH_CHILD = ':nth-child',
PSEUDO_NTH_LAST_CHILD = ':nth-last-child',
PSEUDO_NTH_LAST_OF_TYPE = ':nth-last-of-type',
PSEUDO_NTH_OF_TYPE = ':nth-of-type',
PSEUDO_ONLY_OF_TYPE = ':only-of-type',
PSEUDO_ONLY_CHILD = ':only-child',
/**
* Object to gain quick access to the selectors that required children
*
* @property PSEUDO_REQUIRED_CHILDREN
* @default {
* ':first-child': true,
* ':first-of-type': true,
* ':last-child': true,
* ':last-of-type': true,
* ':nth-child': true,
* ':nth-last-child': true,
* ':nth-last-of-type': true,
* ':nth-of-type': true,
* ':only-of-type': true,
* ':only-child': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
PSEUDO_REQUIRED_CHILDREN = createHashMap(),
_matchesSelectorItem, _matchesOneSelector, _findElementSibling, vNodeProto, _markRemoved, _tryReplaceChild,
_splitSelector, _findNodeSibling, _matchNthChild, _batchEmit, _emitDestroyChildren, _tryRemoveDomNode;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_CHILD] = true;
/**
* Searches for the next -or previous- node-sibling (nodeType of 1, 3 or 8).
*
* @method _findNodeSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findNodeSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
index = vParent.vChildNodes.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildNodes[index];
};
/**
* Searches for the next -or previous- Element-sibling (nodeType of 1).
*
* @method _findElementSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findElementSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
if (vnode.nodeType===1) {
index = vParent.vChildren.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildren[index];
}
else {
/*jshint noempty:true */
while ((vnode=_findNodeSibling(vnode, next)) && (vnode.nodeType!==1)) {}
/*jshint noempty:false */
return vnode;
}
};
/**
* Check whether the vnode matches a "nth-child" test, which is used for css pseudoselectors like `nth-child`, `nth-of-type` etc.
*
* @method _matchNthChild
* @param pseudoArg {String} the argument for nth-child
* @param index {Number} the index of the inspected vnode
* @return {Boolean} whether the vnode matches the nthChild test
* @protected
* @private
* @since 0.0.1
*/
_matchNthChild = function(pseudoArg, index) {
var match, k, a, b, nodeOk, nthIndex, sign, isNumber;
(pseudoArg==='even') && (pseudoArg='2n');
(pseudoArg==='odd') && (pseudoArg='2n+1');
match = pseudoArg.match(NTH_CHILD_REGEXP) || (isNumber=pseudoArg.validateNumber());
if (!match) {
return false;
}
// pseudoArg follows the pattern: `an+b`
if (!isNumber) {
a = match[1];
sign = match[2];
b = match[3] || 0;
(b==='') && (b=0);
}
else {
b = pseudoArg;
}
sign && (sign=sign[0]);
if (!a) {
// only fixed index to match
return (sign==='-') ? false : (parseInt(b, 10)===index);
}
else {
// we need to iterate
nodeOk = false;
b = window.Number(b);
for (k=0; !nodeOk; k++) {
nthIndex = (sign==='-') ? (a*k) - b : (a*k) + b;
if (nthIndex===index) {
nodeOk = true;
}
else if (nthIndex>index) {
// beyond index --> will never become a fix anymore
return false;
}
}
return nodeOk;
}
};
/**
* Check whether the vnode matches the css-selector. the css-selector should be a single selector,
* not multiple, so it shouldn't contain a `comma`.
*
* @method _matchesOneSelector
* @param vnode {vnode} the vnode to inspect
* @param selector {String} the selector-item to check the match for
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches the css-selector
* @protected
* @private
* @since 0.0.1
*/
_matchesOneSelector = function(vnode, selector, relatedVNode) {
var selList = _splitSelector(selector),
size = selList.length,
selMatch = false,
i, selectorItem, last,
rightvnode, relationMatch, checkRelation;
if (STORABLE_SPLIT_CHARACTER[selList[size-1]]) {
return false;
}
relationMatch = function(leftVNode, rightVNode, relationCharacter) {
var match, vParent, vChildren;
// when `selector` starts with `>`, `~` or `+`, then
// there should also be a match comparing a related node!
switch (relationCharacter) {
case '>':
leftVNode || (leftVNode=rightVNode.vParent);
match = (leftVNode.vChildren.indexOf(rightVNode)!==-1);
break;
case '~':
leftVNode || (leftVNode=rightVNode.vFirstElement);
vParent = leftVNode.vParent;
vChildren = vParent && vParent.vChildren;
match = vParent && (vChildren.indexOf(leftVNode)<vChildren.indexOf(rightVNode));
break;
case '+':
leftVNode || (leftVNode=rightVNode.vPreviousElement);
match = (leftVNode.vNextElement === rightVNode);
}
return !!match;
};
last = size - 1;
i = last;
while (vnode && ((selMatch || (i===last)) && (i>=0))) {
selectorItem = selList[i];
if (STORABLE_SPLIT_CHARACTER[selectorItem]) {
checkRelation = selectorItem;
i--;
}
else {
selMatch = _matchesSelectorItem(vnode, selectorItem);
if (!selMatch && (i===last)) {
return false;
}
while (!selMatch && vnode && (i!==last)) {
// may also look at nodes higher in the chain
vnode = SIBLING_MATCH_CHARACTER[selList[i+1]] ? vnode.vPreviousElement : vnode.vParent;
selMatch = vnode && _matchesSelectorItem(vnode, selectorItem);
}
selMatch && checkRelation && vnode && (selMatch=relationMatch(vnode, rightvnode, checkRelation));
rightvnode = vnode;
if (vnode) {
// do a precheck on the next checkRelation:
// and determine whether to set the vnode upper the tree or at the previous sibling:
vnode = SIBLING_MATCH_CHARACTER[selList[i-1]] ? vnode.vPreviousElement : vnode.vParent;
}
if (selMatch) {
checkRelation = false;
i--;
}
}
}
if ((rightvnode===relatedVNode) || (i!==-1)) {
selMatch = false;
}
selMatch && checkRelation && rightvnode && (selMatch=relationMatch(relatedVNode, rightvnode, checkRelation));
return selMatch;
};
/**
* Check whether the vnode matches one specific selector-item. Suppose the css-selector: "#mynode li.red .blue"
* then there are 3 selector-items: "#mynode", "li.red" and ".blue"
*
* This method also can handle the new selectors:
* <ul>
* <li>[att^=val] –-> the “begins with” selector</li>
* <li>[att$=val] –-> the “ends with” selector</li>
* <li>[att*=val] –-> the “contains” selector (might be a substring)</li>
* <li>[att~=val] –-> the “contains” selector as a separate word, separated by spaces</li>
* <li>[att|=val] –-> the “contains” selector as a separate word, separated by `|`</li>
* <li>+ --> (same level)</li>
* <li>~ --> (same level)</li>
* </ul>
*
* @method _matchesSelectorItem
* @param vnode {Object} the vnode to inspect
* @param selectorItem {String} the selector-item to check the match for
* @return {Boolean} whether the vnode matches the selector-item
* @protected
* @private
* @since 0.0.1
*/
_matchesSelectorItem = function (vnode, selectorItem) {
var i = 0,
len = selectorItem.length,
character = selectorItem[0],
tagName, id, className, attributeName, attributeValue, stringMarker, attributeisString, isBoolean, insideAttributeValue, insideAttribute,
vParent, checkBoolean, treatment, k, min, max, value, len2, index, found, pseudo, pseudoArg, arglevel, count, vParentVChildren;
if (selectorItem==='*') {
return true;
}
if (!SELECTOR_IDENTIFIERS[character]) {
// starts with tagName
tagName = '';
// reposition i to continue in the right way:
i--;
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
tagName += character;
}
if (tagName.toUpperCase()!==vnode.tag) {
return false;
}
}
while (i<len) {
switch (character) {
case '#':
id = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
id += character;
}
if (id!==vnode.id) {
return false;
}
break;
case '.':
className = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
className += character;
}
if (!vnode.hasClass(className)) {
return false;
}
break;
case '[':
attributeName = '';
while ((++i<len) && (character=selectorItem[i]) && !END_ATTRIBUTENAME[character]) {
attributeName += character;
}
// if character===']' then we have an attribute without a value-definition
if (!vnode.attrs[attributeName] || ((character===']') && (vnode.attrs[attributeName]!==''))) {
return !!vnode.attrs[attributeName];
}
// now we read the value of the attribute
// however, it could be that the selector has a special `detailed` identifier set (defined by: ATTR_DETAIL_SPECIFIERS)
if (ATTR_DETAIL_SPECIFIERS[character]) {
treatment = character; // store the character to know how the attributedata should be treaded
i++; // character should be a "=" by now
}
else {
treatment = null;
}
attributeValue = '';
stringMarker = selectorItem[i+1];
attributeisString = (stringMarker==='"') || (stringMarker==="'");
attributeisString && (i++);
// end of attributaValue = (character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker))
while ((++i<len) && (character=selectorItem[i]) && !((character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker)))) {
attributeValue += character;
}
if (attributeisString) {
// if attribute is string, then we need to _remove to last stringmarker
attributeValue = attributeValue.substr(0, attributeValue.length-1);
}
else {
// if attribute is no string, then we need to typecast its value
isBoolean = ((attributeValue.length>3) && (attributeValue.length<6) &&
(checkBoolean=attributeValue.toUpperCase()) &&
((checkBoolean==='FALSE') || (checkBoolean==='TRUE')));
// typecast the value to either Boolean or Number:
attributeValue = isBoolean ? (checkBoolean==='TRUE') : parseFloat(attributeValue);
}
// depending upon how the attributedata should be treated:
if (treatment) {
switch (treatment) {
case '^': // “begins with” selector
if (!vnode.attrs[attributeName].startsWith(attributeValue)) {
return false;
}
break;
case '$': // “ends with” selector
if (!vnode.attrs[attributeName].endsWith(attributeValue)) {
return false;
}
break;
case '*': // “contains” selector (might be a substring)
if (!vnode.attrs[attributeName].contains(attributeValue)) {
return false;
}
break;
case '~': // “contains” selector as a separate word, separated by spaces
if (!(' '+vnode.attrs[attributeName]+' ').contains(' '+attributeValue+' ')) {
return false;
}
break;
case '|': // “contains” selector as a separate word, separated by `|`
if (!('|'+vnode.attrs[attributeName]+'|').contains('|'+attributeValue+'|')) {
return false;
}
break;
}
}
else if (vnode.attrs[attributeName]!==attributeValue) {
return false;
}
// we still need to increase one position:
(++i<len) && (character=selectorItem[i]);
break;
case ':':
// we have a pseudo-selector
// first, find out which one
// because '::' is a valid start (though without any selection), we start to back the next character as well:
pseudo = ':'+selectorItem[++i];
pseudoArg = '';
vParent = vnode.vParent;
vParentVChildren = vParent && vParent.vChildren;
// pseudo-selectors might have an argument passed in, like `:nth-child(2n+1)` or `:not([type="checkbox"])` --> we
// store this argument inside `pseudoArg`
// also note that combinations are possible with `:not` --> `:not(:nth-child(2n+1))`
// also note that we cannot "just" look for a closing character when running into the usage of attributes:
// for example --> `:not([data-x="some data :)"])`
// that's why -once we are inside attribute-data- we need to continue until the attribute-data ends
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
if (character==='(') {
// starting arguments
arglevel = 1;
insideAttribute = false;
insideAttributeValue = false;
while ((++i<len) && (character=selectorItem[i]) && (arglevel>0)) {
if (!insideAttribute) {
if (character==='(') {
arglevel++;
}
else if (character===')') {
arglevel--;
}
else if (character==='[') {
insideAttribute = true;
}
}
else {
// inside attribute
if (!insideAttributeValue) {
if ((character==='"') || (character==="'")) {
insideAttributeValue = true;
stringMarker = character;
}
else if (character===']') {
insideAttribute = false;
}
}
else if ((character===stringMarker) && (selectorItem[i+1]===']')) {
insideAttributeValue = false;
}
}
(arglevel>0) && (pseudoArg+=character);
}
}
else {
pseudo += character;
}
}
// now, `pseudo` is known as well as its possible pseudoArg
if (!vParentVChildren && PSEUDO_REQUIRED_CHILDREN[pseudo]) {
return false;
}
switch (pseudo) {
case ':checked': // input:checked Selects every checked <input> element
if (!vnode.attrs.checked) {
return false;
}
break;
case ':disabled': // input:disabled Selects every disabled <input> element
if (!vnode.attrs.disabled) {
return false;
}
break;
case ':empty': // p:empty Selects every <p> element that has no children (including text nodes)
if (vnode.vChildNodes && (vnode.vChildNodes.length>0)) {
return false;
}
break;
case ':enabled': // input:enabled Selects every enabled <input> element
if (vnode.attrs.disabled) {
return false;
}
break;
case PSEUDO_FIRST_CHILD: // p:first-child Selects every <p> element that is the first child of its parent
if (vParentVChildren[0]!==vnode) {
return false;
}
break;
case PSEUDO_FIRST_OF_TYPE: // p:first-of-type Selects every <p> element that is the first <p> element of its parent
for (k=vParentVChildren.indexOf(vnode)-1; k>=0; k--) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':focus': // input:focus Selects the input element which has focus
if (vnode.domNode!==DOCUMENT.activeElement) {
return false;
}
break;
case ':in-range': // input:in-range Selects input elements with a value within a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || (value<min) || (value>max)) {
return false;
}
break;
case ':lang': // p:lang(it) Selects every <p> element with a lang attribute equal to "it" (Italian)
if (vnode.attrs.lang!==pseudoArg) {
return false;
}
break;
case PSEUDO_LAST_CHILD: // p:last-child Selects every <p> element that is the last child of its parent
if (vParentVChildren[vParentVChildren.length-1]!==vnode) {
return false;
}
break;
case PSEUDO_LAST_OF_TYPE: // p:last-of-type Selects every <p> element that is the last <p> element of its parent
len2 = vParentVChildren.length;
for (k=vParentVChildren.indexOf(vnode)+1; k<len2; k++) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':not': // :not(p) Selects every element that is not a <p> element
if (vnode.matchesSelector(pseudoArg)) {
return false;
}
break;
case PSEUDO_NTH_CHILD: // p:nth-child(2) Selects every <p> element that is the second child of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
index = vParentVChildren.indexOf(vnode)+1;
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_CHILD: // p:nth-last-child(2) Selects every <p> element that is the second child of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_OF_TYPE: // p:nth-last-of-type(2) Selects every <p> element that is the second <p> element of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
index = 0;
for (k=vParentVChildren.length-1; (k>=0) && !found; k--) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_OF_TYPE: // p:nth-of-type(2) Selects every <p> element that is the second <p> element of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
len2 = vParentVChildren.length;
index = 0;
for (k=0; (k<len2) && !found; k++) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_ONLY_OF_TYPE: // p:only-of-type Selects every <p> element that is the only <p> element of its parent
len2 = vParentVChildren.length;
count = 0;
for (k=0; (k<len2) && (count<=1); k++) {
(vParentVChildren[k].tag===vnode.tag) && count++;
}
if (count!==1) {
return false;
}
break;
case PSEUDO_ONLY_CHILD: // p:only-child Selects every <p> element that is the only child of its parent
if (vParentVChildren.length!==1) {
return false;
}
break;
case ':optional': // input:optional Selects input elements with no "required" attribute
if (vnode.attrs.required) {
return false;
}
break;
case ':out-of-range': // input:out-of-range Selects input elements with a value outside a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || ((value>=min) && (value<=max))) {
return false;
}
break;
case ':read-only': // input:read-only Selects input elements with the "readonly" attribute specified
if (!vnode.attrs.readonly) {
return false;
}
break;
case ':read-write': // input:read-write Selects input elements with the "readonly" attribute NOT specified
if (vnode.attrs.readonly) {
return false;
}
break;
case ':required': // input:required Selects input elements with the "required" attribute specified
if (!vnode.attrs.required) {
return false;
}
break;
case ':root': // Selects the document's root element
if (vnode.domNode!==DOCUMENT.documentElement) {
return false;
}
break;
}
}
}
return true;
};
/**
* Splits the selector into separate subselector-items that should match different elements through the tree.
* Special characters '>' and '+' are added as separate items in the hash.
*
* @method _splitSelector
* @param selector {String} the selector-item to check the match for
* @return {Array} splitted selectors
* @protected
* @private
* @since 0.0.1
*/
_splitSelector = function(selector) {
var list = [],
len = selector.length,
sel = '',
i, character, insideDataAttr;
for (i=0; i<len; i++) {
character = selector[i];
if (character==='[') {
sel += character;
insideDataAttr = true;
}
else if (character===']') {
sel += character;
insideDataAttr = false;
}
else if (insideDataAttr || !SPLIT_CHARACTER[character]) {
sel += character;
}
else {
// unique selectoritem is found, add it to the list
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
// in case the last character was '>', '+' or '~', we need to add it as a separate item
STORABLE_SPLIT_CHARACTER[character] && (list[list.length]=character);
}
}
// add the last item
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
return list;
};
_batchEmit = function() {
// we will exactly define the 'UI:'-event --> Itags will have another emitterName
MUTATION_EVENTS.each(function (mutationEvents, vnode) {
var domNode = vnode.domNode;
if (mutationEvents[EV_REMOVED]) {
domNode.emit('UI:'+EV_REMOVED);
}
else if (mutationEvents[EV_INSERTED]) {
domNode.emit('UI:'+EV_INSERTED);
}
else {
// contentchange and attributechanges can go hand in hand
mutationEvents.each(function(value, evt) {
domNode.emit('UI:'+evt, (evt===EV_CONTENT_CHANGE) ? null : {changed: value});
});
}
});
MUTATION_EVENTS.clear();
BATCH_WILL_RUN = false;
};
_emitDestroyChildren = function(vnode) {
var children = vnode.vChildren,
len = children.length,
i, vChild;
for (i=0; i<len; i++) {
vChild = children[i];
vChild._emit(EV_REMOVED);
}
};
_markRemoved = function(vnode) {
var vChildNodes = vnode.vChildNodes,
len, i, vChildNode;
if (vnode.nodeType===1) {
Object.protectedProp(vnode, 'removedFromDOM', true);
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && _markRemoved(vChildNode);
}
}
}
};
/**
* A safe way to remove a dom-node, even if it is not present.
* Will not throw a JS error when the "to be removed" node isn't in the dom
*
* @method _tryRemoveDomNode
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param childDomNode {DOMNode} the node that needs to be removed
* @since 0.0.1
*/
_tryRemoveDomNode = function(parentDomNode, childDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && childDomNode.isItag && childDomNode.isItag()) {
DOCUMENT._itagList.remove(childDomNode);
}
try {
parentDomNode._removeChild(childDomNode);
}
catch(err) {}
};
/**
* A safe way to replace a dom-node, even if the "to be replaced"-node it is not present.
* Will not throw a JS error when the "to be replaced" node isn't in the dom. in that case, the new node will
* be appended
*
* @method _tryReplaceChild
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param newChildDomNode {DOMNode} the node that needs to be replaced
* @param oldChildDomNode {DOMNode} the node that needs to be inserted
* @since 0.0.1
*/
_tryReplaceChild = function(parentDomNode, newChildDomNode, oldChildDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && oldChildDomNode.isItag && oldChildDomNode.isItag()) {
DOCUMENT._itagList.remove(oldChildDomNode);
}
try {
parentDomNode._replaceChild(newChildDomNode, oldChildDomNode);
}
catch(err) {
// if the childDomNode isn;t there any more - for whatever reason - the we need to append the newChildNode
parentDomNode._appendChild(newChildDomNode);
}
};
vNodeProto = window._ITSAmodules.VNode = {
/**
* Check whether the vnode's domNode is equal, or contains the specified Element.
*
* @method contains
* @return {Boolean} whether the vnode's domNode is equal, or contains the specified Element.
* @since 0.0.1
*/
contains: function(otherVNode, noProtectedSearch) {
var instance = this;
if (otherVNode && (otherVNode.destroyed || (noProtectedSearch && otherVNode._systemNode))) {
return false;
}
while (otherVNode && (otherVNode!==instance)) {
otherVNode = otherVNode.vParent;
if (otherVNode && noProtectedSearch && (otherVNode._systemNode || (otherVNode.isItag && otherVNode.domNode.contentHidden))) {
return false;
}
}
return (otherVNode===instance);
},
/**
* Empties the vnode.
*
* Syncs with the dom.
*
* @method empty
* @param [full=false] {Boolean} whether system-nodes should be removed as well
* @chainable
* @since 0.0.1
*/
empty: function(full) {
delete this._scripts;
return this._setChildNodes([], null, full);
},
/**
* Returns the first child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method firstOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the first child-vnode or null when not present
* @since 0.0.1
*/
firstOfVChildren: function(cssSelector) {
var instance = this,
found, i, len, vChildren, element;
if (!cssSelector) {
return instance.vFirstElementChild;
}
vChildren = instance.vChildren;
len = vChildren.length;
for (i=0; !found && (i<len); i++) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
return found;
},
/**
* Gets the innerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* Only valid for nodetype=1 (HTMLElements)
*
* @method getHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String|undefined} the innerHTML without the elements specified, or `undefined` when not an HTMLElement
* @since 0.0.1
*/
getHTML: function(exclude, includeSystemNodes) {
var instance = this,
html, vChildNodes, len, i, vChildNode;
if (instance.nodeType===1) {
if (exclude) {
Array.isArray(exclude) || (exclude=[exclude]);
}
html = '';
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
switch (vChildNode.nodeType) {
case 1:
(exclude && exclude.contains(vChildNode.domNode)) || (!includeSystemNodes && vChildNode._systemNode) || (html+=vChildNode.getOuterHTML(exclude, includeSystemNodes));
break;
case 3:
html += vChildNode.text.replace(/</g, '<').replace(/>/g, '>');
break;
case 8:
html += '<!--' + vChildNode.text.replace(/</g, '<').replace(/>/g, '>') + '-->';
}
}
}
return html;
},
/**
* Gets the outerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* @method getOuterHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String} the outerHTML
* @since 0.0.1
*/
getOuterHTML: function(exclude, includeSystemNodes) {
var instance = this,
html,
attrs = instance.attrs;
if (instance.nodeType===1) {
if (instance.nodeType!==1) {
return instance.textContent;
}
html = '<' + instance.tag.toLowerCase();
attrs.each(function(value, key) {
html += ' '+key+'="'+value+'"';
});
instance.isVoid && (html += '/');
html += '>';
if (!instance.isVoid) {
html += ((!includeSystemNodes && instance.isItag) ? '' : instance.getHTML(exclude, includeSystemNodes)) + '</' + instance.tag.toLowerCase() + '>';
}
}
return html;
},
/**
* Checks whether the vnode has any vChildNodes (nodeType of 1, 3 or 8).
*
* @method hasVChildNodes
* @return {Boolean} whether the vnode has any vChildNodes.
* @since 0.0.1
*/
hasVChildNodes: function() {
return this.vChildNodes ? (this.vChildNodes.length>0) : false;
},
/**
* Checks whether the vnode has any vChildren (vChildNodes with nodeType of 1).
*
* @method hasVChildren
* @return {Boolean} whether the vnode has any vChildren.
* @since 0.0.1
*/
hasVChildren: function() {
return this.vChildNodes ? (this.vChildren.length>0) : false;
},
/**
* Checks whether the className is present on the vnode.
*
* @method hasClass
* @param className {String|Array} the className to check for. May be an Array of classNames, which all needs to be present.
* @return {Boolean} whether the className (or classNames) is present on the vnode
* @since 0.0.1
*/
hasClass: function(className) {
var instance = this,
check = function(cl) {
return !!instance.classNames[cl];
};
if (!instance.classNames) {
return false;
}
if (typeof className === STRING) {
return check(className);
}
else if (Array.isArray(className)) {
return className.every(check);
}
return false;
},
/**
* Returns the last child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method lastOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the last child-vnode or null when not present
* @since 0.0.1
*/
lastOfVChildren: function(cssSelector) {
var vChildren = this.vChildren,
found, i, element;
if (vChildren) {
if (!cssSelector) {
return this.vLastElementChild;
}
for (i=vChildren.length-1; !found && (i>=0); i--) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
}
return found;
},
/**
* Checks whether the vnode matches one of the specified selectors. `selectors` can be one, or multiple css-selectors,
* separated by a `comma`. For example: "#myid li.red blue" is one selector, "div.red, div.blue, div.green" are three selectors.
*
* @method matchesSelector
* @param selectors {String} one or more css-selectors
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches one of the selectors
* @since 0.0.1
*/
matchesSelector: function(selectors, relatedVNode) {
var instance = this;
if (instance.nodeType!==1) {
return false;
}
selectors = selectors.split(',');
// we can use Array.some, because there won't be many separated selectoritems,
// so the final invocation won't be delayed much compared to looping
return selectors.some(function(selector) {
return _matchesOneSelector(instance, selector, relatedVNode);
});
},
/**
* Reloads the DOM-attribute into the vnode.
*
* @method matchesSelector
* @param attributeName {String} the name of the attribute to be reloaded.
* @return {Node} the domNode that was reloaded.
* @since 0.0.1
*/
reloadAttr: function(attributeName) {
var instance = this,
domNode = instance.domNode,
attributeValue = domNode._getAttribute(attributeName),
attrs = instance.attrs,
extractStyle, extractClass;
if (instance.nodeType===1) {
attributeValue || (attributeValue='');
if (attributeValue==='') {
delete attrs[attributeName];
// in case of STYLE attributeName --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attributeName --> special treatment
(attributeName===CLASS) && (instance.classNames={});
// in case of ID attributeName --> special treatment
if ((attributeName===ID) && (instance.id)) {
delete nodeids[instance.id];
delete instance.id;
}
}
else {
attrs[attributeName] = attributeValue;
// in case of STYLE attributeName --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(attributeValue);
attributeValue = extractStyle.attrStyle;
if (attributeValue) {
attrs.style = attributeValue;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attributeName --> special treatment
extractClass = extractor.extractClass(attributeValue);
attributeValue = extractClass.attrClass;
if (attributeValue) {
attrs[CLASS] = attributeValue;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (instance.id!==attributeValue) && (delete nodeids[instance.id]);
instance.id = attributeValue;
nodeids[attributeValue] = domNode;
}
}
}
return domNode;
},
/**
* Returns the vnode's style in a serialized form: the way it appears in the dom.
*
* @method serializeStyles
* @return {String} vnode's style
* @since 0.0.1
*/
serializeStyles: function() {
return extractor.serializeStyles(this.styles);
},
/**
* Sets the vnode's and dom-nodes inner HTML.
*
* Syncs with the dom. Can be invoked multiple times without issues.
*
* @method setHTML
* @param content {String} the innerHTML
* @param [suppressItagRender] {Boolean} to suppress Itags from rendering
* @param [allowScripts=false] {Boolean} whether scripts are allowed --> these should be defined with `xscript` instead of `script`
* @chainable
* @since 0.0.1
*/
setHTML: function(content, suppressItagRender, allowScripts) {
var instance = this;
instance._setChildNodes(htmlToVNodes(content, vNodeProto, instance.ns, null, suppressItagRender, allowScripts), suppressItagRender);
return instance;
},
/**
* Syncs the vnode's nodeid (if available) inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom. Can be invoked multiple times without issues.
*
* @method storeId
* @chainable
* @since 0.0.1
*/
storeId: function() {
// store node/vnode inside WeakMap:
var instance = this;
instance.id ? (nodeids[instance.id]=instance.domNode) : (delete nodeids[instance.id]);
return instance;
},
//---- private ------------------------------------------------------------------
_addToTaglist: function() {
var instance = this,
itagList;
if (instance.isItag) {
itagList = DOCUMENT.getItags(); // also reads the dom if the list isn't build yet
instance._data || Object.protectedProp(instance, '_data', {});
if (!instance._data.ce_destroyed && !itagList.contains(instance.domNode)) {
itagList.push(instance.domNode);
}
}
},
/**
* Adds a vnode to the end of the list of vChildNodes.
*
* Syncs with the DOM.
*
* @method _appendChild
* @param VNode {vnode} vnode to append
* @private
* @return {Node} the Node that was appended
* @since 0.0.1
*/
_appendChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
size, noProccessScript, scriptContent;
if ((VNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (VNode.attrs && VNode.attrs.src) ? VNode.attrs.src : VNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
VNode._moveToParent(instance);
instance.domNode._appendChild(domNode);
if (VNode.nodeType===3) {
size = instance.vChildNodes.length;
instance._normalize();
// if the size changed, then the domNode was merged
(size===instance.vChildNodes.length) || (domNode=instance.vChildNodes[instance.vChildNodes.length-1].domNode);
}
if (VNode.nodeType===1) {
VNode._addToTaglist();
VNode._emit(EV_INSERTED);
}
return domNode;
}
},
/**
* Removes the vnode from its parent vChildNodes- and vChildren-list.
*
* Does NOT sync with the dom.
*
* @method _deleteFromParent
* @private
* @chainable
* @since 0.0.1
*/
_deleteFromParent: function() {
var instance = this,
vParent = instance.vParent;
if (vParent && vParent.vChildNodes) {
vParent.vChildNodes.remove(instance);
// force to recalculate the vChildren on a next call:
(instance.nodeType===1) && (vParent._vChildren=null);
}
return instance;
},
/**
* Cleans up (empties) vnode's `_data`
*
* @method _cleanData
* @private
* @chainable
* @since 0.0.1
*/
_cleanData: function() {
var instance = this,
data = instance._data;
data && data.each(
function(value, key) {
delete data[key];
}
);
return instance;
},
/**
* Cleans up (removes) duplicated `style` definitions.
*
* @method _cleanupStyle
* @private
* @chainable
* @since 0.0.1
*/
_cleanupStyle: function() {
var instance = this,
compare = [],
removal = [],
vChildren = instance.vChildren,
len = vChildren.length,
vChild, i, styleContent, vChildInner;
for (i=0; i<len; i++) {
vChild = vChildren[i];
if (vChild.tag==='STYLE') {
vChildInner = vChild.vChildNodes[0];
if (vChildInner) {
styleContent = vChildInner.text;
if (compare.contains(styleContent)) {
removal[removal.length] = vChild;
}
else {
compare[compare.length] = styleContent;
}
}
else {
// empty style-tag
removal[removal.length] = vChild;
}
}
}
removal.forEach(function(vnode) {
instance._removeChild(vnode);
});
},
/**
* Destroys the vnode and all its vnode-vChildNodes.
* Removes it from its vParent.vChildNodes list,
* also removes its definitions inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom.
*
* @method _destroy
* @private
* @chainable
* @since 0.0.1
*/
_destroy: function(silent) {
var instance = this,
vChildNodes = instance.vChildNodes,
len, i, vChildNode, vParent, treeNodes;
if (!instance.destroyed) {
if (!silent) {
// Because we don't want to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
later(function() {
instance._emit(EV_REMOVED, null, null, null, true);
}, 5);
}
Object.protectedProp(instance, 'destroyed', true);
// first: determine the dom-tree, which module `event-dom` needs to determine where the node was before it was destroyed:
treeNodes = [instance];
vParent = instance.vParent;
while (vParent) {
treeNodes[treeNodes.length] = vParent;
vParent = vParent.vParent;
}
// mark all its vChildNodes so we can see if the node is in the DOM
_markRemoved(instance);
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && instance.isItag) {
DOCUMENT._itagList.remove(instance.domNode);
}
instance._scripts && (instance._scripts.length=0);
// The definite cleanup needs to be done after a timeout:
// someone might need to handle the Element when removed (fe to cleanup specific things)
later(function() {
instance._cleanData();
if (instance.nodeType===1) {
// _destroy all its vChildNodes
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && vChildNode._destroy(true);
}
}
}
instance._vChildren = null;
// explicitely set instance.domNode._vnode and instance.domNode to null in order to prevent problems with the GC (we break the circular reference)
delete instance.domNode._vnode;
// if valid id, then _remove the DOMnodeRef from internal hash
instance.id && delete nodeids[instance.id];
}, silent ? 0 : DESTROY_DELAY);
instance._deleteFromParent();
// Do not make domNode `null` --> it could be used even when not in the dom
}
return instance;
},
_emit: function(evt, attribute, newValue, prevValue, destroyEvt) {
/**
* Emitted by every Element that gets inserted.
*
* @event nodeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets removed.
*
* @event noderemove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets its content changed (innerHTML/innerText).
*
* @event nodecontentchange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute inserted.
*
* @event attributeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* </ul>
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute removed.
*
* @event attributeremove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Strings of the attributeNames that are removed
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute changed.
*
* @event attributechange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* <li>prevValue</li>
* </ul>
* @since 0.1
*/
var instance = this,
silent, attrMutations, mutationEvents, mutation, vParent;
if (!DOCUMENT.hasMutationSubs || (instance.nodeType!==1)) {
return;
}
silent = !!DOCUMENT._suppressMutationEvents;
if (!silent && (destroyEvt || !instance.destroyed)) {
// Because we don't wannt to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
mutationEvents = MUTATION_EVENTS.get(instance) || {};
if (attribute) {
attrMutations = mutationEvents[evt] || [];
if (evt===EV_ATTRIBUTE_REMOVED) {
mutation = attribute;
}
else {
mutation = {
attribute: attribute
};
if ((evt===EV_ATTRIBUTE_INSERTED) || (evt===EV_ATTRIBUTE_CHANGED)) {
mutation.newValue = newValue;
}
if ((evt===EV_ATTRIBUTE_CHANGED) && prevValue) {
mutation.prevValue = prevValue;
}
}
attrMutations.push(mutation);
mutationEvents[evt] = attrMutations;
}
else {
mutationEvents[evt] = true;
}
MUTATION_EVENTS.set(instance, mutationEvents);
// now set all parent to have a nodecontentchange:
vParent = instance.vParent;
vParent && vParent._emit(EV_CONTENT_CHANGE);
// in case of removal we need to emit EV_REMOVED for all children right now
// for they will be actually removed silently after a delay of 1 minute
(evt===EV_REMOVED) && _emitDestroyChildren(instance);
if (!BATCH_WILL_RUN) {
BATCH_WILL_RUN = true;
async(function() {
_batchEmit();
});
}
}
return instance;
},
/**
* Inserts `newVNode` before `refVNode`.
*
* Syncs with the DOM.
*
* @method _insertBefore
* @param newVNode {vnode} vnode to insert
* @param refVNode {vnode} The vnode before which newVNode should be inserted.
* @private
* @return {Node} the Node being inserted (equals domNode)
* @since 0.0.1
*/
_insertBefore: function(newVNode, refVNode) {
var instance = this,
domNode = newVNode.domNode,
index = instance.vChildNodes.indexOf(refVNode),
noProccessScript, scriptContent;
if ((newVNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (newVNode.attrs && newVNode.attrs.src) ? newVNode.attrs.src : newVNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
if (index!==-1) {
newVNode._moveToParent(instance, index);
instance.domNode._insertBefore(domNode, refVNode.domNode);
(newVNode.nodeType===3) && instance._normalize();
if (newVNode.nodeType===1) {
newVNode._addToTaglist();
newVNode._emit(EV_INSERTED);
}
return domNode;
}
else {
console.warn('trying to insert before, but no ref-node found. Will append the new node');
return instance._appendChild(newVNode);
}
}
},
/**
* Moves the vnode from its current parent.vChildNodes list towards a new parent vnode at the specified position.
*
* Does NOT sync with the dom.
*
* @method _moveToParent
* @param parentVNode {vnode} the parent-vnode
* @param [index] {Number} the position of the child. When not specified, it will be appended.
* @private
* @chainable
* @since 0.0.1
*/
_moveToParent: function(parentVNode, index) {
var instance = this,
vParent = instance.vParent;
instance._deleteFromParent();
instance.vParent = parentVNode;
parentVNode.vChildNodes || (parentVNode.vChildNodes=[]);
(typeof index==='number') ? parentVNode.vChildNodes.insertAt(instance, index) : (parentVNode.vChildNodes[parentVNode.vChildNodes.length]=instance);
// force to recalculate the vChildren on a next call:
vParent && (instance.nodeType===1) && (vParent._vChildren = null);
// force to recalculate the vChildren on a next call:
parentVNode && (instance.nodeType===1) && (parentVNode._vChildren=null);
return instance;
},
/**
* Removes empty TextNodes and merges following TextNodes inside the vnode.
*
* Syncs with the dom.
*
* @method _normalize
* @private
* @chainable
* @since 0.0.1
*/
_normalize: function() {
var instance = this,
domNode = instance.domNode,
vChildNodes = instance.vChildNodes,
changed = false,
i, preChildNode, vChildNode;
if (!instance._unNormalizable && vChildNodes) {
for (i=vChildNodes.length-1; i>=0; i--) {
vChildNode = vChildNodes[i];
preChildNode = vChildNodes[i-1]; // i will get the value `-1` eventually, which leads into undefined preChildNode
if (vChildNode.nodeType===3) {
if (vChildNode.text==='') {
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
else if (preChildNode && preChildNode.nodeType===3) {
preChildNode.text += vChildNode.text;
preChildNode.domNode.nodeValue = unescapeEntities(preChildNode.text);
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
}
}
}
changed && instance._emit(EV_CONTENT_CHANGE);
return instance;
},
/**
* Makes the vnode `normalizable`. Could be set to `false` when batch-inserting nodes, while `normalizaing` manually at the end.
* Afterwards, you should always reset `normalizable` to true.
*
* @method _normalizable
* @param value {Boolean} whether the vnode should be normalisable.
* @private
* @chainable
* @since 0.0.1
*/
_normalizable: function(value) {
var instance = this;
value ? (delete instance._unNormalizable) : (instance._unNormalizable=true);
return instance;
},
/**
* Prevents MutationObserver from making the dom sync with the vnode.
* Should be used when manipulating the dom from within the vnode itself (to preventing looping)
*
* @method _noSync
* @chainable
* @private
* @since 0.0.1
*/
_noSync: function() {
var instance = this;
if (!instance._nosync) {
instance._nosync = true;
async(function() {
instance._nosync = false;
});
}
return instance;
},
/**
* Removes the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _removeAttr
* @param attributeName {String}
* @private
* @chainable
* @since 0.0.1
*/
_removeAttr: function(attributeName, suppressItagRender) {
var instance = this,
attributeNameSplitted, ns;
if (instance.isItag && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
console.warn('Not allowed to remove the attribute '+attributeName);
return instance;
}
if (instance.attrs[attributeName]!==undefined) {
delete instance.attrs[attributeName];
// in case of STYLE attribute --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attribute --> special treatment
(attributeName===CLASS) && (instance.classNames={});
if (attributeName===ID) {
delete nodeids[instance.id];
delete instance.id;
}
instance._emit(EV_ATTRIBUTE_REMOVED, attributeName);
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
instance.domNode._removeAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName);
}
else {
instance.domNode._removeAttribute(attributeName);
}
}
return instance;
},
/**
* Removes the vnode's child-vnode from its vChildren and the DOM.
*
* Syncs with the DOM.
*
* @method removeChild
* @param VNode {vnode} the child-vnode to remove
* @private
* @since 0.0.1
*/
_removeChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
hadFocus = domNode && domNode.hasFocus && domNode.hasFocus() && (VNode.attrs['fm-lastitem']==='true'),
parentVNode = VNode.vParent;
VNode._destroy();
_tryRemoveDomNode(instance.domNode, VNode.domNode);
instance._normalize();
// now, reset the focus on focusmanager when needed:
if (hadFocus) {
while (parentVNode && !parentVNode.attrs['fm-manage']) {
parentVNode = parentVNode.vParent;
}
parentVNode && parentVNode.domNode.focus();
}
},
/**
* Replaces the current vnode at the parent.vChildNode list by `newVNode`
*
* Does NOT sync with the dom.
*
* @method _replaceAtParent
* @param newVNode {Object} the new vnode which should take over the place of the current vnode
* @private
* @chainable
* @since 0.0.1
*/
_replaceAtParent: function(newVNode) {
var instance = this,
vParent = instance.vParent,
vChildNodes, index;
if (vParent && (vChildNodes=vParent.vChildNodes)) {
index = vChildNodes.indexOf(instance);
// force to recalculate the vChildren on a next call:
((instance.nodeType===1) || (newVNode.nodeType===1)) && (instance.vParent._vChildren=null);
vChildNodes[index] = newVNode;
}
return instance._destroy();
},
/**
* Sets the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _setAttr
* @param attributeName {String}
* @param value {String} the value for the attributeName
* @param [force=false] {Boolean} force the attribute to be set, even if restrictions would deny it
* @private
* @chainable
* @since 0.0.1
*/
_setAttr: function(attributeName, value, force, suppressItagRender) {
var instance = this,
extractStyle, extractClass,
attrs = instance.attrs,
prevVal = attrs[attributeName],
domNode = instance.domNode,
attributeNameSplitted, ns;
if (instance.isItag && !force && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
if (prevVal!=value) {
console.warn('Not allowed to set the attribute '+attributeName);
}
return instance;
}
// don't check by !== --> value isn't parsed into a String yet
if (prevVal && ((value===undefined) || (value===null))) {
instance._removeAttr(attributeName, suppressItagRender);
return instance;
}
// attribute-values are always Strings:
value = String(value);
// attribute-values will be stored without " or '
value = value.replace(/"/g, '"').replace(/'/g, "'");
if (prevVal!=value) {
attrs[attributeName] = value;
// in case of STYLE attribute --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(value);
value = extractStyle.attrStyle;
if (value) {
attrs.style = value;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attribute --> special treatment
extractClass = extractor.extractClass(value);
value = extractClass.attrClass;
if (value) {
attrs[CLASS] = value;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (delete nodeids[instance.id]);
instance.id = value;
nodeids[value] = domNode;
}
instance._emit(prevVal ? EV_ATTRIBUTE_CHANGED : EV_ATTRIBUTE_INSERTED, attributeName, value, prevVal);
// when set in the dom --> quotes need to be set as "
value = value.replace(/"/g, '"');
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
domNode._setAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName, value);
}
else {
domNode._setAttribute(attributeName, value);
}
}
return instance;
},
/**
* Redefines the attributes of both the vnode as well as its related dom-node. The new
* definition replaces any previous attributes (without touching unmodified attributes).
* the `is` attribute cannot be changed for itags.
*
* Syncs the new vnode's attributes with the dom.
*
* @method _setAttrs
* @param newAttrs {Object|Array} the new attributes to be set
* @private
* @chainable
* @since 0.0.1
*/
_setAttrs: function(newAttrs, suppressItagRender) {
// does sync the DOM
var instance = this,
attrsObj, attr, attrs, i, key, keys, len, value;
if (instance.nodeType!==1) {
return;
}
instance._noSync();
attrs = instance.attrs;
attrs.id && (delete nodeids[attrs.id]);
if (Object.isObject(newAttrs)) {
attrsObj = newAttrs;
}
else {
attrsObj = {};
len = newAttrs.length;
for (i=0; i<len; i++) {
attr = newAttrs[i];
attrsObj[attr.name] = attr.value;
}
}
if (instance.isItag && !suppressItagRender) {
if (attrs.is) {
attrsObj.is = attrs.is;
}
else {
delete attrsObj.is;
delete attrsObj.Is;
delete attrsObj.iS;
delete attrsObj.IS;
}
}
// first _remove the attributes that are no longer needed.
// quickest way for object iteration: http://jsperf.com/object-keys-iteration/20
keys = Object.keys(attrs);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
attrsObj[key] || instance._removeAttr(key, suppressItagRender);
}
// next: every attribute that differs: redefine
keys = Object.keys(attrsObj);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
value = attrsObj[key];
(attrs[key]===value) || instance._setAttr(key, value, suppressItagRender);
}
return instance;
},
/**
* Redefines the childNodes of both the vnode as well as its related dom-node. The new
* definition replaces any previous nodes. (without touching unmodified nodes).
*
* Syncs the new vnode's childNodes with the dom.
*
* @method _setChildNodes
* @param newVChildNodes {Array} array with vnodes which represent the new childNodes
* @private
* @chainable
* @since 0.0.1
*/
_setChildNodes: function(newVChildNodes, suppressItagRender, removeSystemElements) {
// does sync the DOM
var instance = this,
vChildNodes = instance.vChildNodes || [],
domNode = instance.domNode,
forRemoval = [],
i, oldChild, newChild, newLength, len, len2, childDomNode, nodeswitch, bkpAttrs, cleanupStyle,
process, bkpChildNodes, needNormalize, prevSuppress, scriptContent, _scripts, scriptLen, j;
instance._noSync();
// first: reset ._vChildren --> by making it empty, its getter will refresh its list on a next call
instance._vChildren = null;
// if newVChildNodes is undefined, then we assume it to be empty --> an empty array
newVChildNodes || (newVChildNodes=[]);
// quickest way to loop through array is by using for loops: http://jsperf.com/array-foreach-vs-for-loop/5
len = vChildNodes.length;
// we need to add the systemNodes -if any- to the new newVChildNodes: they should be retained.
// SystemNodes are always places at the beginning, so they won't get reshuffled:
if (!removeSystemElements) {
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
if (oldChild._systemNode) {
newVChildNodes.insertAt(oldChild, i);
}
else {
break;
}
}
}
newLength = newVChildNodes.length;
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
childDomNode = oldChild.domNode;
if (i < newLength) {
newChild = newVChildNodes[i];
newChild.vParent || (newChild.vParent=instance);
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
/*jshint boss:true */
switch (nodeswitch=NODESWITCH[oldChild.nodeType][newChild.nodeType]) {
/*jshint boss:false */
case 1: // oldNodeType==Element, newNodeType==Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
if ((oldChild.tag!==newChild.tag) ||
((oldChild.tag===newChild.tag) && oldChild.isItag && (oldChild.attrs.is!==newChild.attrs.is))) {
// new tag --> completely replace
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
/*jshint proto:true */
oldChild.isItag && oldChild.domNode.destroyUI && oldChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
newChild.attrs = {}; // reset to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
oldChild._replaceAtParent(newChild);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
else {
// same tag --> only update what is needed
// NOTE: when this._unchangableAttrs exists, an itag-element syncs its UI -->
if (oldChild._data) {
// we might need to set the class `itag-rendered` when the attributeData says so:
// this happens when an itag gets refreshed with an unrendered definition
if (oldChild._data.itagRendered && !newChild.hasClass('itag-rendered')) {
newChild.classNames['itag-rendered'] = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' '+'itag-rendered';
}
else {
newChild.attrs[CLASS] = 'itag-rendered';
}
}
// we might need to set the class `focussed` when the attributeData says so:
// this happens when an itag gets rerendered: its renderFn doesn't know if any elements
// were focussed
if (oldChild._data.focussed && !newChild.hasClass('focussed')) {
newChild.classNames.focussed = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' ' + 'focussed';
}
else {
newChild.attrs[CLASS] = 'focussed';
}
}
if (oldChild._data['fm-tabindex']) {
// node has the tabindex set by the focusmanager,
// but that info might got lost with re-rendering of the new element
newChild.attrs.tabindex = '0';
}
}
if (oldChild.isItag) {
prevSuppress = DOCUMENT._suppressMutationEvents || false;
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
/*jshint proto:true */
newChild.domNode.destroyUI && newChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
oldChild._setAttrs(newChild.attrs, suppressItagRender);
newChild._destroy(true); // destroy through the vnode and removing from DOCUMENT._itagList
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(prevSuppress);
}
else {
oldChild._setAttrs(newChild.attrs, suppressItagRender);
// next: sync the vChildNodes:
oldChild._setChildNodes(newChild.vChildNodes, suppressItagRender);
}
// reset ref. to the domNode, for it might have been changed by newChild:
oldChild.id && (nodeids[oldChild.id]=childDomNode);
newVChildNodes[i] = oldChild;
}
}
break;
case 2: // oldNodeType==Element, newNodeType==TextNode
// case2 and case3 should be treated the same
case 3: // oldNodeType==Element, newNodeType==Comment
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
oldChild._replaceAtParent(newChild);
instance._emit(EV_CONTENT_CHANGE);
break;
case 4: // oldNodeType==TextNode, newNodeType==Element
// case4 and case7 should be treated the same
case 7: // oldNodeType==Comment, newNodeType==Element
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
// oldChild.isVoid = newChild.isVoid;
// delete oldChild.text;
instance._emit(EV_CONTENT_CHANGE);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
break;
case 5: // oldNodeType==TextNode, newNodeType==TextNode
// case5 and case9 should be treated the same
case 9: // oldNodeType==Comment, newNodeType==Comment
if (oldChild.text!==newChild.text) {
oldChild.text = newChild.text;
oldChild.domNode.nodeValue = unescapeEntities(newChild.text);
instance._emit(EV_CONTENT_CHANGE);
}
newVChildNodes[i] = oldChild;
break;
case 6: // oldNodeType==TextNode, newNodeType==Comment
// case6 and case8 should be treated the same
case 8: // oldNodeType==Comment, newNodeType==TextNode
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = oldChild.vParent;
instance._emit(EV_CONTENT_CHANGE);
}
if ((nodeswitch===2) || (nodeswitch===5) || (nodeswitch===8)) {
needNormalize = true;
}
}
else {
// _remove previous definition
_tryRemoveDomNode(domNode, oldChild.domNode);
// the oldChild needs to be removed, however, this cannot be done right now, for it would effect the loop
// so we store it inside a hash to remove it later
forRemoval[forRemoval.length] = oldChild;
}
}
// now definitely remove marked childNodes:
len2 = forRemoval.length;
for (i=0; i<len2; i++) {
forRemoval[i]._destroy();
}
// now we add all new vChildNodes that go beyond `len`:
for (i = len; i < newLength; i++) {
newChild = newVChildNodes[i];
newChild.vParent = instance;
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
switch (newChild.nodeType) {
case 1: // Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
domNode._appendChild(newChild.domNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
break;
case 3: // TextNode
needNormalize = true;
// we need to break through --> no `break`
/* falls through */
default: // TextNode or CommentNode
// newChild.domNode.nodeValue = newChild.text;
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
domNode._appendChild(newChild.domNode);
instance._emit(EV_CONTENT_CHANGE);
}
newChild.storeId();
}
instance.vChildNodes = newVChildNodes;
needNormalize && instance._normalize();
cleanupStyle && instance._cleanupStyle();
return instance;
},
_setUnchangableAttrs: function(unchangableObj) {
this._unchangableAttrs = unchangableObj;
}
};
//---- properties ------------------------------------------------------------------
/**
* A hash of all the `attributes` of the vnode's representing dom-node.
*
* @property attrs
* @type Object
* @since 0.0.1
*/
/**
* Hash with all the classes of the vnode. Every class represents a key, all values are set `true`.
*
* @property classNames
* @type Object
* @since 0.0.1
*/
/**
* The `id` of the vnode's representing dom-node (if any).
*
* @property id
* @type String
* @since 0.0.1
*/
/**
* Tells whether tag is a void Element. Examples are: `br`, `img` and `input`. Non-void Elements are f.e. `div` and `table`.
* For TextNodes and CommentNodes, this property is `undefined`.
*
* @property isVoid
* @type Boolean
* @since 0.0.1
*/
/**
* The `nodeType` of the vnode's representing dom-node (1===ElementNode, 3===TextNode, 8===CommentNode).
*
* @property nodeType
* @type Number
* @since 0.0.1
*/
/**
* The `tag` of the vnode's representing dom-node (allways uppercase).
*
* @property tag
* @type String
* @since 0.0.1
*/
/**
* The `content` of the vnode's representing dom-node, in case it is a TextNode or CommentNode.
* Equals dom-node.nodeValue.
*
* Is `undefined` for ElementNodes.
*
* @property text
* @type String
* @since 0.0.1
*/
/**
* Hash with all the childNodes (vnodes). vChildNodes are any kind of vnodes (nodeType===1, 3 or 8)
*
* @property vChildNodes
* @type Array
* @since 0.0.1
*/
/**
* The underlying `dom-node` that the vnode represents.
*
* @property domNode
* @type domNode
* @since 0.0.1
*/
/**
* vnode's parentNode (defined as a vnode itself).
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
Object.defineProperties(vNodeProto, {
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property innerHTML
* @type String
* @since 0.0.1
*/
innerHTML: {
get: function() {
return this.getHTML();
},
set: function(v) {
this.setHTML(v);
}
},
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property nodeValue
* @type String
* @since 0.0.1
*/
nodeValue: {
get: function() {
var instance = this;
return ((instance.nodeType===3) || (instance.nodeType===8)) ? instance.text : null;
},
set: function(v) {
var instance = this,
newTextContent, prevTextContent;
if ((instance.nodeType===3) || (instance.nodeType===8)) {
prevTextContent = instance.domNode.textContent;
instance.domNode.textContent = v;
// set .text AFTER the dom-node is updated --> the content might be escaped!
newTextContent = instance.text = instance.domNode.textContent;
(newTextContent!==prevTextContent) && instance._emit(EV_CONTENT_CHANGE);
}
}
},
/**
* Gets or sets the outerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property outerHTML
* @type String
* @since 0.0.1
*/
outerHTML: {
get: function() {
return this.getOuterHTML();
},
set: function(v) {
var instance = this,
vParent = instance.vParent,
id = instance.attrs.id,
vnode, vnodes, bkpAttrs, bkpChildNodes, i, len, vChildNodes, isLastChildNode, index, refDomNode;
if ((instance.nodeType!==1) || !vParent) {
return;
}
instance._noSync();
vChildNodes = vParent.vChildNodes;
index = vChildNodes.indexOf(instance);
isLastChildNode = (index===(vChildNodes.length-1));
isLastChildNode || (refDomNode=vChildNodes[index+1].domNode);
vnodes = htmlToVNodes(v, vNodeProto, vParent.ns, vParent);
len = vnodes.length;
if (len>0) {
// the first vnode will replace the current instance:
vnode = vnodes[0];
if (vnode.nodeType===1) {
if (vnode.tag!==instance.tag) {
// new tag --> completely replace
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
id && (delete nodeids[id]);
vnode.attrs = {}; // reset to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
// vnode.attrs = bkpAttrs;
// vnode.vChildNodes = bkpChildNodes;
vnode.id && (nodeids[vnode.id]=vnode.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
else {
instance._setAttrs(vnode.attrs);
instance._setChildNodes(vnode.vChildNodes);
}
}
else {
id && (delete nodeids[id]);
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
}
for (i=1; i<len; i++) {
vnode = vnodes[i];
switch (vnode.nodeType) {
case 1: // Element
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
vnode.attrs = {}; // reset, to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._insertBefore(vnode.domNode, refDomNode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
break;
default: // TextNode or CommentNode
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._appendChild(vnode.domNode, refDomNode);
}
vnode.storeId();
vnode._moveToParent(vParent, index+i);
}
}
},
/**
* Gets or sets the innerContent of the Node as plain text.
*
* The setter syncs with the DOM.
*
* @property textContent
* @type String
* @since 0.0.1
*/
textContent: {
get: function() {
var instance = this,
text = '',
vChildNodes = instance.vChildNodes,
len, i, vChildNode;
if (instance.nodeType===1) {
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
text += (vChildNode.nodeType===3) ? vChildNode.text : ((vChildNode.nodeType===1) ? vChildNode.textContent : '');
}
}
else {
text = instance.text;
}
return text;
},
set: function(v) {
var vnode = Object.create(vNodeProto);
vnode.domNode = DOCUMENT.createTextNode(v);
// create circular reference:
vnode.domNode._vnode = vnode;
vnode.nodeType = 3;
vnode.text = vnode.domNode.textContent;
this._setChildNodes([vnode]);
}
},
/**
* Hash with all the children (vnodes). vChildren are vnodes that have a representing dom-node that is an HtmlElement (nodeType===1)
*
* @property vChildren
* @type Array
* @since 0.0.1
*/
vChildren: {
get: function() {
var instance = this,
children = instance._vChildren,
vChildNode, vChildNodes, i, len;
vChildNodes = instance.vChildNodes;
if (vChildNodes && !children) {
children = instance._vChildren = [];
len = vChildNodes.length;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
(vChildNode.nodeType===1) && (children[children.length]=vChildNode);
}
}
children || (children = instance._vChildren = []);
return children;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirst
* @type vnode
* @since 0.0.1
*/
vFirst: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstChild;
}
},
/**
* Reference to the first vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirstChild
* @type vnode
* @since 0.0.1
*/
vFirstChild: {
get: function() {
return (this.vChildNodes && this.vChildNodes[0]) || null;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vFirstElement
* @type vnode
* @since 0.0.1
*/
vFirstElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstElementChild;
}
},
/**
* Reference to the first vChild, where the related dom-node an Element (nodeType===1).
*
* @property vFirstElementChild
* @type vnode
* @since 0.0.1
*/
vFirstElementChild: {
get: function() {
return this.vChildren[0] || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLast
* @type vnode
* @since 0.0.1
*/
vLast: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastChild;
}
},
/**
* Reference to the last vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLastChild
* @type vnode
* @since 0.0.1
*/
vLastChild: {
get: function() {
var vChildNodes = this.vChildNodes;
return (vChildNodes && vChildNodes[vChildNodes.length-1]) || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vLastElement
* @type vnode
* @since 0.0.1
*/
vLastElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastElementChild;
}
},
/**
* Reference to the last vChild, where the related dom-node an Element (nodeType===1).
*
* @property vLastElementChild
* @type vnode
* @since 0.0.1
*/
vLastElementChild: {
get: function() {
var vChildren = this.vChildren;
return vChildren[vChildren.length-1] || null;
}
},
/**
* the Parent vnode
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
/**
* Reference to the next of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vNext
* @type vnode
* @since 0.0.1
*/
vNext: {
get: function() {
return _findNodeSibling(this, true);
}
},
/**
* Reference to the next of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vNextElement
* @type vnode
* @since 0.0.1
*/
vNextElement: {
get: function() {
return _findElementSibling(this, true);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vPrevious
* @type vnode
* @since 0.0.1
*/
vPrevious: {
get: function() {
return _findNodeSibling(this);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vPreviousElement
* @type vnode
* @since 0.0.1
*/
vPreviousElement: {
get: function() {
return _findElementSibling(this);
}
}
});
return vNodeProto;
};
},{"./attribute-extractor.js":3572,"./html-parser.js":3576,"./vdom-ns.js":3578,"js-ext/extra/hashmap.js":3530,"js-ext/extra/lightmap.js":3531,"js-ext/lib/array.js":3532,"js-ext/lib/object.js":3533,"js-ext/lib/string.js":3535,"polyfill":3552,"utils/lib/timers.js":3555}],3580:[function(require,module,exports){
arguments[4][100][0].apply(exports,arguments)
},{"./partials/extend-document.js":3574,"./partials/extend-element.js":3575,"./partials/node-parser.js":3577,"js-ext/extra/hashmap.js":3530,"js-ext/lib/object.js":3533,"utils/lib/timers.js":3555}],3581:[function(require,module,exports){
module.exports=require(4)
},{}],3582:[function(require,module,exports){
module.exports=require(202)
},{"./lib/array.js":3583,"./lib/function.js":3584,"./lib/json.js":3585,"./lib/object.js":3586,"./lib/promise.js":3587,"./lib/string.js":3588}],3583:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":3591}],3584:[function(require,module,exports){
module.exports=require(204)
},{"polyfill/polyfill-base.js":3591}],3585:[function(require,module,exports){
module.exports=require(205)
},{"polyfill/polyfill-base.js":3591}],3586:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3581,"polyfill/polyfill-base.js":3591,"utils":3592}],3587:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":3591}],3588:[function(require,module,exports){
module.exports=require(29)
},{}],3589:[function(require,module,exports){
module.exports=require(14)
},{}],3590:[function(require,module,exports){
module.exports=require(15)
},{}],3591:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3589,"./lib/window.console.js":3590}],3592:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3593,"./lib/timers.js":3594}],3593:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3581,"polyfill/polyfill-base.js":3597}],3594:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3597}],3595:[function(require,module,exports){
module.exports=require(14)
},{}],3596:[function(require,module,exports){
module.exports=require(15)
},{}],3597:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3595,"./lib/window.console.js":3596}],3598:[function(require,module,exports){
arguments[4][5][0].apply(exports,arguments)
},{"event-dom":3599,"js-ext/extra/classes.js":3694,"js-ext/extra/hashmap.js":3695,"js-ext/lib/object.js":3696,"js-ext/lib/promise.js":3697,"js-ext/lib/string.js":3698,"polyfill":3710,"utils/lib/timers.js":3711,"vdom":3767}],3599:[function(require,module,exports){
module.exports=require(267)
},{"event":3603,"js-ext/extra/hashmap.js":3619,"js-ext/lib/array.js":3620,"js-ext/lib/object.js":3621,"js-ext/lib/string.js":3622,"polyfill/polyfill-base.js":3634,"utils":3635,"vdom":3693}],3600:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":3605,"js-ext/lib/object.js":3606,"polyfill/polyfill-base.js":3618}],3601:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":3600}],3602:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":3600,"js-ext/extra/classes.js":3604,"js-ext/lib/object.js":3606}],3603:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":3600,"./event-emitter.js":3601,"./event-listener.js":3602}],3604:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":3606,"js-ext/extra/hashmap.js":3605,"polyfill/polyfill-base.js":3609}],3605:[function(require,module,exports){
module.exports=require(4)
},{}],3606:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3605,"polyfill/polyfill-base.js":3609,"utils":3610}],3607:[function(require,module,exports){
module.exports=require(14)
},{}],3608:[function(require,module,exports){
module.exports=require(15)
},{}],3609:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3607,"./lib/window.console.js":3608}],3610:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3611,"./lib/timers.js":3612}],3611:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3605,"polyfill/polyfill-base.js":3615}],3612:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3615}],3613:[function(require,module,exports){
module.exports=require(14)
},{}],3614:[function(require,module,exports){
module.exports=require(15)
},{}],3615:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3613,"./lib/window.console.js":3614}],3616:[function(require,module,exports){
module.exports=require(14)
},{}],3617:[function(require,module,exports){
module.exports=require(15)
},{}],3618:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3616,"./lib/window.console.js":3617}],3619:[function(require,module,exports){
module.exports=require(4)
},{}],3620:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":3625}],3621:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3619,"polyfill/polyfill-base.js":3625,"utils":3626}],3622:[function(require,module,exports){
module.exports=require(29)
},{}],3623:[function(require,module,exports){
module.exports=require(14)
},{}],3624:[function(require,module,exports){
module.exports=require(15)
},{}],3625:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3623,"./lib/window.console.js":3624}],3626:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3627,"./lib/timers.js":3628}],3627:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3619,"polyfill/polyfill-base.js":3631}],3628:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3631}],3629:[function(require,module,exports){
module.exports=require(14)
},{}],3630:[function(require,module,exports){
module.exports=require(15)
},{}],3631:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3629,"./lib/window.console.js":3630}],3632:[function(require,module,exports){
module.exports=require(14)
},{}],3633:[function(require,module,exports){
module.exports=require(15)
},{}],3634:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3632,"./lib/window.console.js":3633}],3635:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3636,"./lib/timers.js":3637}],3636:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3619,"polyfill/polyfill-base.js":3640}],3637:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3640}],3638:[function(require,module,exports){
module.exports=require(14)
},{}],3639:[function(require,module,exports){
module.exports=require(15)
},{}],3640:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3638,"./lib/window.console.js":3639}],3641:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],3642:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":3646,"js-ext/extra/hashmap.js":3643,"polyfill/polyfill-base.js":3652}],3643:[function(require,module,exports){
module.exports=require(4)
},{}],3644:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":3645,"../lib/object.js":3646,"./classes.js":3642,"js-ext/extra/hashmap.js":3643,"polyfill/lib/weakmap.js":3650}],3645:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":3652}],3646:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3643,"polyfill/polyfill-base.js":3652,"utils":3653}],3647:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":3652}],3648:[function(require,module,exports){
module.exports=require(29)
},{}],3649:[function(require,module,exports){
module.exports=require(14)
},{}],3650:[function(require,module,exports){
module.exports=require(57)
},{}],3651:[function(require,module,exports){
module.exports=require(15)
},{}],3652:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3649,"./lib/window.console.js":3651}],3653:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3654,"./lib/timers.js":3655}],3654:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3643,"polyfill/polyfill-base.js":3658}],3655:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3658}],3656:[function(require,module,exports){
module.exports=require(14)
},{}],3657:[function(require,module,exports){
module.exports=require(15)
},{}],3658:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3656,"./lib/window.console.js":3657}],3659:[function(require,module,exports){
module.exports=require(66)
},{}],3660:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":3659}],3661:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":3659}],3662:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":3659}],3663:[function(require,module,exports){
module.exports=require(14)
},{}],3664:[function(require,module,exports){
module.exports=require(15)
},{}],3665:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3663,"./lib/window.console.js":3664}],3666:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3667,"./lib/timers.js":3668}],3667:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3643,"polyfill/polyfill-base.js":3671}],3668:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3671}],3669:[function(require,module,exports){
module.exports=require(14)
},{}],3670:[function(require,module,exports){
module.exports=require(15)
},{}],3671:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3669,"./lib/window.console.js":3670}],3672:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":3673}],3673:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":3674,"js-ext/lib/object.js":3675}],3674:[function(require,module,exports){
module.exports=require(4)
},{}],3675:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3674,"polyfill/polyfill-base.js":3678,"utils":3679}],3676:[function(require,module,exports){
module.exports=require(14)
},{}],3677:[function(require,module,exports){
module.exports=require(15)
},{}],3678:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3676,"./lib/window.console.js":3677}],3679:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3680,"./lib/timers.js":3681}],3680:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3674,"polyfill/polyfill-base.js":3684}],3681:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3684}],3682:[function(require,module,exports){
module.exports=require(14)
},{}],3683:[function(require,module,exports){
module.exports=require(15)
},{}],3684:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3682,"./lib/window.console.js":3683}],3685:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":3643,"js-ext/lib/object.js":3646,"js-ext/lib/string.js":3648,"polyfill":3665,"polyfill/extra/transition.js":3660,"polyfill/extra/vendorCSS.js":3662}],3686:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":3643,"js-ext/lib/object.js":3646,"polyfill":3665}],3687:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":3643,"js-ext/lib/object.js":3646,"js-ext/lib/string.js":3648,"polyfill":3665}],3688:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":3641,"./attribute-extractor.js":3685,"./element-array.js":3686,"./html-parser.js":3689,"./node-parser.js":3690,"./vdom-ns.js":3691,"./vnode.js":3692,"js-ext/extra/hashmap.js":3643,"js-ext/lib/object.js":3646,"js-ext/lib/promise.js":3647,"js-ext/lib/string.js":3648,"polyfill":3665,"polyfill/extra/transition.js":3660,"polyfill/extra/transitionend.js":3661,"polyfill/extra/vendorCSS.js":3662,"utils":3666,"window-ext":3672}],3689:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":3685,"./vdom-ns.js":3691,"js-ext/extra/hashmap.js":3643,"js-ext/lib/object.js":3646,"polyfill":3665}],3690:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":3685,"./vdom-ns.js":3691,"./vnode.js":3692,"js-ext/extra/hashmap.js":3643,"js-ext/lib/object.js":3646,"polyfill":3665}],3691:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":3643,"js-ext/lib/object.js":3646,"polyfill":3665}],3692:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":3685,"./html-parser.js":3689,"./vdom-ns.js":3691,"js-ext/extra/hashmap.js":3643,"js-ext/extra/lightmap.js":3644,"js-ext/lib/array.js":3645,"js-ext/lib/object.js":3646,"js-ext/lib/string.js":3648,"polyfill":3665,"utils/lib/timers.js":3668}],3693:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":3687,"./partials/extend-element.js":3688,"./partials/node-parser.js":3690,"js-ext/extra/hashmap.js":3643,"js-ext/lib/object.js":3646,"utils/lib/timers.js":3668}],3694:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":3696,"js-ext/extra/hashmap.js":3695,"polyfill/polyfill-base.js":3701}],3695:[function(require,module,exports){
module.exports=require(4)
},{}],3696:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3695,"polyfill/polyfill-base.js":3701,"utils":3702}],3697:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":3701}],3698:[function(require,module,exports){
module.exports=require(29)
},{}],3699:[function(require,module,exports){
module.exports=require(14)
},{}],3700:[function(require,module,exports){
module.exports=require(15)
},{}],3701:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3699,"./lib/window.console.js":3700}],3702:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3703,"./lib/timers.js":3704}],3703:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3695,"polyfill/polyfill-base.js":3707}],3704:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3707}],3705:[function(require,module,exports){
module.exports=require(14)
},{}],3706:[function(require,module,exports){
module.exports=require(15)
},{}],3707:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3705,"./lib/window.console.js":3706}],3708:[function(require,module,exports){
module.exports=require(14)
},{}],3709:[function(require,module,exports){
module.exports=require(15)
},{}],3710:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3708,"./lib/window.console.js":3709}],3711:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3714}],3712:[function(require,module,exports){
module.exports=require(14)
},{}],3713:[function(require,module,exports){
module.exports=require(15)
},{}],3714:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3712,"./lib/window.console.js":3713}],3715:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],3716:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":3720,"js-ext/extra/hashmap.js":3717,"polyfill/polyfill-base.js":3726}],3717:[function(require,module,exports){
module.exports=require(4)
},{}],3718:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":3719,"../lib/object.js":3720,"./classes.js":3716,"js-ext/extra/hashmap.js":3717,"polyfill/lib/weakmap.js":3724}],3719:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":3726}],3720:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3717,"polyfill/polyfill-base.js":3726,"utils":3727}],3721:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":3726}],3722:[function(require,module,exports){
module.exports=require(29)
},{}],3723:[function(require,module,exports){
module.exports=require(14)
},{}],3724:[function(require,module,exports){
module.exports=require(57)
},{}],3725:[function(require,module,exports){
module.exports=require(15)
},{}],3726:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3723,"./lib/window.console.js":3725}],3727:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3728,"./lib/timers.js":3729}],3728:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3717,"polyfill/polyfill-base.js":3732}],3729:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3732}],3730:[function(require,module,exports){
module.exports=require(14)
},{}],3731:[function(require,module,exports){
module.exports=require(15)
},{}],3732:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3730,"./lib/window.console.js":3731}],3733:[function(require,module,exports){
module.exports=require(66)
},{}],3734:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":3733}],3735:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":3733}],3736:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":3733}],3737:[function(require,module,exports){
module.exports=require(14)
},{}],3738:[function(require,module,exports){
module.exports=require(15)
},{}],3739:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3737,"./lib/window.console.js":3738}],3740:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3741,"./lib/timers.js":3742}],3741:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3717,"polyfill/polyfill-base.js":3745}],3742:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3745}],3743:[function(require,module,exports){
module.exports=require(14)
},{}],3744:[function(require,module,exports){
module.exports=require(15)
},{}],3745:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3743,"./lib/window.console.js":3744}],3746:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":3747}],3747:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":3748,"js-ext/lib/object.js":3749}],3748:[function(require,module,exports){
module.exports=require(4)
},{}],3749:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3748,"polyfill/polyfill-base.js":3752,"utils":3753}],3750:[function(require,module,exports){
module.exports=require(14)
},{}],3751:[function(require,module,exports){
module.exports=require(15)
},{}],3752:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3750,"./lib/window.console.js":3751}],3753:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3754,"./lib/timers.js":3755}],3754:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3748,"polyfill/polyfill-base.js":3758}],3755:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3758}],3756:[function(require,module,exports){
module.exports=require(14)
},{}],3757:[function(require,module,exports){
module.exports=require(15)
},{}],3758:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3756,"./lib/window.console.js":3757}],3759:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":3717,"js-ext/lib/object.js":3720,"js-ext/lib/string.js":3722,"polyfill":3739,"polyfill/extra/transition.js":3734,"polyfill/extra/vendorCSS.js":3736}],3760:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":3717,"js-ext/lib/object.js":3720,"polyfill":3739}],3761:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":3717,"js-ext/lib/object.js":3720,"js-ext/lib/string.js":3722,"polyfill":3739}],3762:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":3715,"./attribute-extractor.js":3759,"./element-array.js":3760,"./html-parser.js":3763,"./node-parser.js":3764,"./vdom-ns.js":3765,"./vnode.js":3766,"js-ext/extra/hashmap.js":3717,"js-ext/lib/object.js":3720,"js-ext/lib/promise.js":3721,"js-ext/lib/string.js":3722,"polyfill":3739,"polyfill/extra/transition.js":3734,"polyfill/extra/transitionend.js":3735,"polyfill/extra/vendorCSS.js":3736,"utils":3740,"window-ext":3746}],3763:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":3759,"./vdom-ns.js":3765,"js-ext/extra/hashmap.js":3717,"js-ext/lib/object.js":3720,"polyfill":3739}],3764:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":3759,"./vdom-ns.js":3765,"./vnode.js":3766,"js-ext/extra/hashmap.js":3717,"js-ext/lib/object.js":3720,"polyfill":3739}],3765:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":3717,"js-ext/lib/object.js":3720,"polyfill":3739}],3766:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":3759,"./html-parser.js":3763,"./vdom-ns.js":3765,"js-ext/extra/hashmap.js":3717,"js-ext/extra/lightmap.js":3718,"js-ext/lib/array.js":3719,"js-ext/lib/object.js":3720,"js-ext/lib/string.js":3722,"polyfill":3739,"utils/lib/timers.js":3742}],3767:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":3761,"./partials/extend-element.js":3762,"./partials/node-parser.js":3764,"js-ext/extra/hashmap.js":3717,"js-ext/lib/object.js":3720,"utils/lib/timers.js":3742}],3768:[function(require,module,exports){
module.exports=require(14)
},{}],3769:[function(require,module,exports){
module.exports=require(15)
},{}],3770:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3768,"./lib/window.console.js":3769}],3771:[function(require,module,exports){
module.exports=require(4)
},{}],3772:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3771,"polyfill/polyfill-base.js":3776,"utils":3777}],3773:[function(require,module,exports){
module.exports=require(29)
},{}],3774:[function(require,module,exports){
module.exports=require(14)
},{}],3775:[function(require,module,exports){
module.exports=require(15)
},{}],3776:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3774,"./lib/window.console.js":3775}],3777:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3778,"./lib/timers.js":3779}],3778:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3771,"polyfill/polyfill-base.js":3782}],3779:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3782}],3780:[function(require,module,exports){
module.exports=require(14)
},{}],3781:[function(require,module,exports){
module.exports=require(15)
},{}],3782:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3780,"./lib/window.console.js":3781}],3783:[function(require,module,exports){
module.exports=require(14)
},{}],3784:[function(require,module,exports){
module.exports=require(15)
},{}],3785:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3783,"./lib/window.console.js":3784}],3786:[function(require,module,exports){
module.exports=require(567)
},{"js-ext/extra/hashmap.js":3771,"js-ext/lib/object.js":3772,"js-ext/lib/string.js":3773,"polyfill":3785}],3787:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],3788:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":3792,"js-ext/extra/hashmap.js":3789,"polyfill/polyfill-base.js":3798}],3789:[function(require,module,exports){
module.exports=require(4)
},{}],3790:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":3791,"../lib/object.js":3792,"./classes.js":3788,"js-ext/extra/hashmap.js":3789,"polyfill/lib/weakmap.js":3796}],3791:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":3798}],3792:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3789,"polyfill/polyfill-base.js":3798,"utils":3799}],3793:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":3798}],3794:[function(require,module,exports){
module.exports=require(29)
},{}],3795:[function(require,module,exports){
module.exports=require(14)
},{}],3796:[function(require,module,exports){
module.exports=require(57)
},{}],3797:[function(require,module,exports){
module.exports=require(15)
},{}],3798:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3795,"./lib/window.console.js":3797}],3799:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3800,"./lib/timers.js":3801}],3800:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3789,"polyfill/polyfill-base.js":3804}],3801:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3804}],3802:[function(require,module,exports){
module.exports=require(14)
},{}],3803:[function(require,module,exports){
module.exports=require(15)
},{}],3804:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3802,"./lib/window.console.js":3803}],3805:[function(require,module,exports){
module.exports=require(66)
},{}],3806:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":3805}],3807:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":3805}],3808:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":3805}],3809:[function(require,module,exports){
module.exports=require(14)
},{}],3810:[function(require,module,exports){
module.exports=require(15)
},{}],3811:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3809,"./lib/window.console.js":3810}],3812:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3813,"./lib/timers.js":3814}],3813:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3789,"polyfill/polyfill-base.js":3817}],3814:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3817}],3815:[function(require,module,exports){
module.exports=require(14)
},{}],3816:[function(require,module,exports){
module.exports=require(15)
},{}],3817:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3815,"./lib/window.console.js":3816}],3818:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":3819}],3819:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":3820,"js-ext/lib/object.js":3821}],3820:[function(require,module,exports){
module.exports=require(4)
},{}],3821:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3820,"polyfill/polyfill-base.js":3824,"utils":3825}],3822:[function(require,module,exports){
module.exports=require(14)
},{}],3823:[function(require,module,exports){
module.exports=require(15)
},{}],3824:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3822,"./lib/window.console.js":3823}],3825:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3826,"./lib/timers.js":3827}],3826:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3820,"polyfill/polyfill-base.js":3830}],3827:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3830}],3828:[function(require,module,exports){
module.exports=require(14)
},{}],3829:[function(require,module,exports){
module.exports=require(15)
},{}],3830:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3828,"./lib/window.console.js":3829}],3831:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":3789,"js-ext/lib/object.js":3792,"js-ext/lib/string.js":3794,"polyfill":3811,"polyfill/extra/transition.js":3806,"polyfill/extra/vendorCSS.js":3808}],3832:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":3789,"js-ext/lib/object.js":3792,"polyfill":3811}],3833:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":3789,"js-ext/lib/object.js":3792,"js-ext/lib/string.js":3794,"polyfill":3811}],3834:[function(require,module,exports){
arguments[4][95][0].apply(exports,arguments)
},{"../css/element.css":3787,"./attribute-extractor.js":3831,"./element-array.js":3832,"./html-parser.js":3835,"./node-parser.js":3836,"./vdom-ns.js":3837,"./vnode.js":3838,"js-ext/extra/hashmap.js":3789,"js-ext/lib/object.js":3792,"js-ext/lib/promise.js":3793,"js-ext/lib/string.js":3794,"polyfill":3811,"polyfill/extra/transition.js":3806,"polyfill/extra/transitionend.js":3807,"polyfill/extra/vendorCSS.js":3808,"utils":3812,"window-ext":3818}],3835:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":3831,"./vdom-ns.js":3837,"js-ext/extra/hashmap.js":3789,"js-ext/lib/object.js":3792,"polyfill":3811}],3836:[function(require,module,exports){
arguments[4][97][0].apply(exports,arguments)
},{"./attribute-extractor.js":3831,"./vdom-ns.js":3837,"./vnode.js":3838,"js-ext/extra/hashmap.js":3789,"js-ext/lib/object.js":3792,"polyfill":3811}],3837:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":3789,"js-ext/lib/object.js":3792,"polyfill":3811}],3838:[function(require,module,exports){
"use strict";
/**
* Delivers the `vnode` prototype object, which is a virtualisation of an `Element` inside the Dom.
* These Elements work smoothless with the vdom (see ...).
*
* vnodes are much quicker to access and walk through than native dom-nodes. However, this is a module you don't need
* by itself: `Element`-types use these features under the hood.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* <br>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
*
* @module vdom
* @submodule vnode
* @class vnode
* @since 0.0.1
*/
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('js-ext/lib/string.js');
require('polyfill');
var createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.VNode) {
return window._ITSAmodules.VNode; // VNODE was already created
}
console.warn('VDOM 11');
var NS = require('./vdom-ns.js')(window),
extractor = require('./attribute-extractor.js')(window),
DOCUMENT = window.document,
LightMap = require('js-ext/extra/lightmap.js'),
MUTATION_EVENTS = new LightMap(),
BATCH_WILL_RUN = false,
xmlNS = NS.xmlNS,
nodeids = NS.nodeids,
htmlToVNodes = require('./html-parser.js')(window),
timers = require('utils/lib/timers.js'),
async = timers.async,
later = timers.later,
/*jshint proto:true */
PROTO_SUPPORTED = !!Object.__proto__,
/*jshint proto:false */
// cleanup memory after 1 minute: removed nodes SHOULD NOT be accessed afterwards
// because vnode would be recalculated and might be different from before
DESTROY_DELAY = 60000,
unescapeEntities = NS.UnescapeEntities,
NTH_CHILD_REGEXP = /^(?:(\d*)[n|N])([\+|\-](\d+))?$/, // an+b
STRING = 'string',
CLASS = 'class',
STYLE = 'style',
ID = 'id',
NODE= 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
EV_REMOVED = NODE+REMOVE,
EV_INSERTED = NODE+INSERT,
EV_CONTENT_CHANGE = NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = ATTRIBUTE+INSERT,
SPLIT_CHARACTER = createHashMap({
' ': true,
'>': true,
'+': true, // only select the element when it is immediately preceded by the former element
'~': true // only the element when it has the former element as a sibling. (just like `+`, but less strict)
}),
STORABLE_SPLIT_CHARACTER = createHashMap({
'>': true,
'+': true,
'~': true
}),
SIBLING_MATCH_CHARACTER = createHashMap({
'+': true,
'~': true
}),
ATTR_DETAIL_SPECIFIERS = createHashMap({
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to attribute-name end-tokens.
*
* @property END_ATTRIBUTENAME
* @default {
* '=': true,
* ']': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
END_ATTRIBUTENAME = createHashMap({
'=': true,
']': true,
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to different changes of Element nodeType changes.
*
* @property NODESWITCH
* @default {
* 1: {
* 1: 1,
* 3: 2,
* 8: 3
* },
* 3: {
* 1: 4,
* 3: 5,
* 8: 6
* },
* 8: {
* 1: 7,
* 3: 8,
* 8: 9
* }
* }
* @type Object
* @protected
* @since 0.0.1
*/
NODESWITCH = createHashMap({
1: createHashMap({
1: 1, // oldNodeType==Element, newNodeType==Element
3: 2, // oldNodeType==Element, newNodeType==TextNode
8: 3 // oldNodeType==Element, newNodeType==Comment
}),
3: createHashMap({
1: 4, // oldNodeType==TextNode, newNodeType==Element
3: 5, // oldNodeType==TextNode, newNodeType==TextNode
8: 6 // oldNodeType==TextNode, newNodeType==Comment
}),
8: createHashMap({
1: 7, // oldNodeType==Comment, newNodeType==Element
3: 8, // oldNodeType==Comment, newNodeType==TextNode
8: 9 // oldNodeType==Comment, newNodeType==Comment
})
}),
/**
* Object to gain quick access to selector start-tokens.
*
* @property SELECTOR_IDENTIFIERS
* @default {
* '#': 1,
* '.': 2,
* '[': 3
* }
* @type Object
* @protected
* @since 0.0.1
*/
SELECTOR_IDENTIFIERS = createHashMap({
'#': 1,
'.': 2,
'[': 3,
':': 4
}),
PSEUDO_FIRST_CHILD = ':first-child',
PSEUDO_FIRST_OF_TYPE = ':first-of-type',
PSEUDO_LAST_CHILD = ':last-child',
PSEUDO_LAST_OF_TYPE = ':last-of-type',
PSEUDO_NTH_CHILD = ':nth-child',
PSEUDO_NTH_LAST_CHILD = ':nth-last-child',
PSEUDO_NTH_LAST_OF_TYPE = ':nth-last-of-type',
PSEUDO_NTH_OF_TYPE = ':nth-of-type',
PSEUDO_ONLY_OF_TYPE = ':only-of-type',
PSEUDO_ONLY_CHILD = ':only-child',
/**
* Object to gain quick access to the selectors that required children
*
* @property PSEUDO_REQUIRED_CHILDREN
* @default {
* ':first-child': true,
* ':first-of-type': true,
* ':last-child': true,
* ':last-of-type': true,
* ':nth-child': true,
* ':nth-last-child': true,
* ':nth-last-of-type': true,
* ':nth-of-type': true,
* ':only-of-type': true,
* ':only-child': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
PSEUDO_REQUIRED_CHILDREN = createHashMap(),
_matchesSelectorItem, _matchesOneSelector, _findElementSibling, vNodeProto, _markRemoved, _tryReplaceChild,
_splitSelector, _findNodeSibling, _matchNthChild, _batchEmit, _emitDestroyChildren, _tryRemoveDomNode;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_CHILD] = true;
/**
* Searches for the next -or previous- node-sibling (nodeType of 1, 3 or 8).
*
* @method _findNodeSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findNodeSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
index = vParent.vChildNodes.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildNodes[index];
};
/**
* Searches for the next -or previous- Element-sibling (nodeType of 1).
*
* @method _findElementSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findElementSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
if (vnode.nodeType===1) {
index = vParent.vChildren.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildren[index];
}
else {
/*jshint noempty:true */
while ((vnode=_findNodeSibling(vnode, next)) && (vnode.nodeType!==1)) {}
/*jshint noempty:false */
return vnode;
}
};
/**
* Check whether the vnode matches a "nth-child" test, which is used for css pseudoselectors like `nth-child`, `nth-of-type` etc.
*
* @method _matchNthChild
* @param pseudoArg {String} the argument for nth-child
* @param index {Number} the index of the inspected vnode
* @return {Boolean} whether the vnode matches the nthChild test
* @protected
* @private
* @since 0.0.1
*/
_matchNthChild = function(pseudoArg, index) {
var match, k, a, b, nodeOk, nthIndex, sign, isNumber;
(pseudoArg==='even') && (pseudoArg='2n');
(pseudoArg==='odd') && (pseudoArg='2n+1');
match = pseudoArg.match(NTH_CHILD_REGEXP) || (isNumber=pseudoArg.validateNumber());
if (!match) {
return false;
}
// pseudoArg follows the pattern: `an+b`
if (!isNumber) {
a = match[1];
sign = match[2];
b = match[3] || 0;
(b==='') && (b=0);
}
else {
b = pseudoArg;
}
sign && (sign=sign[0]);
if (!a) {
// only fixed index to match
return (sign==='-') ? false : (parseInt(b, 10)===index);
}
else {
// we need to iterate
nodeOk = false;
b = window.Number(b);
for (k=0; !nodeOk; k++) {
nthIndex = (sign==='-') ? (a*k) - b : (a*k) + b;
if (nthIndex===index) {
nodeOk = true;
}
else if (nthIndex>index) {
// beyond index --> will never become a fix anymore
return false;
}
}
return nodeOk;
}
};
/**
* Check whether the vnode matches the css-selector. the css-selector should be a single selector,
* not multiple, so it shouldn't contain a `comma`.
*
* @method _matchesOneSelector
* @param vnode {vnode} the vnode to inspect
* @param selector {String} the selector-item to check the match for
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches the css-selector
* @protected
* @private
* @since 0.0.1
*/
_matchesOneSelector = function(vnode, selector, relatedVNode) {
var selList = _splitSelector(selector),
size = selList.length,
selMatch = false,
i, selectorItem, last,
rightvnode, relationMatch, checkRelation;
if (STORABLE_SPLIT_CHARACTER[selList[size-1]]) {
return false;
}
relationMatch = function(leftVNode, rightVNode, relationCharacter) {
var match, vParent, vChildren;
// when `selector` starts with `>`, `~` or `+`, then
// there should also be a match comparing a related node!
switch (relationCharacter) {
case '>':
leftVNode || (leftVNode=rightVNode.vParent);
match = (leftVNode.vChildren.indexOf(rightVNode)!==-1);
break;
case '~':
leftVNode || (leftVNode=rightVNode.vFirstElement);
vParent = leftVNode.vParent;
vChildren = vParent && vParent.vChildren;
match = vParent && (vChildren.indexOf(leftVNode)<vChildren.indexOf(rightVNode));
break;
case '+':
leftVNode || (leftVNode=rightVNode.vPreviousElement);
match = (leftVNode.vNextElement === rightVNode);
}
return !!match;
};
last = size - 1;
i = last;
while (vnode && ((selMatch || (i===last)) && (i>=0))) {
selectorItem = selList[i];
if (STORABLE_SPLIT_CHARACTER[selectorItem]) {
checkRelation = selectorItem;
i--;
}
else {
selMatch = _matchesSelectorItem(vnode, selectorItem);
if (!selMatch && (i===last)) {
return false;
}
while (!selMatch && vnode && (i!==last)) {
// may also look at nodes higher in the chain
vnode = SIBLING_MATCH_CHARACTER[selList[i+1]] ? vnode.vPreviousElement : vnode.vParent;
selMatch = vnode && _matchesSelectorItem(vnode, selectorItem);
}
selMatch && checkRelation && vnode && (selMatch=relationMatch(vnode, rightvnode, checkRelation));
rightvnode = vnode;
if (vnode) {
// do a precheck on the next checkRelation:
// and determine whether to set the vnode upper the tree or at the previous sibling:
vnode = SIBLING_MATCH_CHARACTER[selList[i-1]] ? vnode.vPreviousElement : vnode.vParent;
}
if (selMatch) {
checkRelation = false;
i--;
}
}
}
if ((rightvnode===relatedVNode) || (i!==-1)) {
selMatch = false;
}
selMatch && checkRelation && rightvnode && (selMatch=relationMatch(relatedVNode, rightvnode, checkRelation));
return selMatch;
};
/**
* Check whether the vnode matches one specific selector-item. Suppose the css-selector: "#mynode li.red .blue"
* then there are 3 selector-items: "#mynode", "li.red" and ".blue"
*
* This method also can handle the new selectors:
* <ul>
* <li>[att^=val] –-> the “begins with” selector</li>
* <li>[att$=val] –-> the “ends with” selector</li>
* <li>[att*=val] –-> the “contains” selector (might be a substring)</li>
* <li>[att~=val] –-> the “contains” selector as a separate word, separated by spaces</li>
* <li>[att|=val] –-> the “contains” selector as a separate word, separated by `|`</li>
* <li>+ --> (same level)</li>
* <li>~ --> (same level)</li>
* </ul>
*
* @method _matchesSelectorItem
* @param vnode {Object} the vnode to inspect
* @param selectorItem {String} the selector-item to check the match for
* @return {Boolean} whether the vnode matches the selector-item
* @protected
* @private
* @since 0.0.1
*/
_matchesSelectorItem = function (vnode, selectorItem) {
var i = 0,
len = selectorItem.length,
character = selectorItem[0],
tagName, id, className, attributeName, attributeValue, stringMarker, attributeisString, isBoolean, insideAttributeValue, insideAttribute,
vParent, checkBoolean, treatment, k, min, max, value, len2, index, found, pseudo, pseudoArg, arglevel, count, vParentVChildren;
if (selectorItem==='*') {
return true;
}
if (!SELECTOR_IDENTIFIERS[character]) {
// starts with tagName
tagName = '';
// reposition i to continue in the right way:
i--;
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
tagName += character;
}
if (tagName.toUpperCase()!==vnode.tag) {
return false;
}
}
while (i<len) {
switch (character) {
case '#':
id = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
id += character;
}
if (id!==vnode.id) {
return false;
}
break;
case '.':
className = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
className += character;
}
if (!vnode.hasClass(className)) {
return false;
}
break;
case '[':
attributeName = '';
while ((++i<len) && (character=selectorItem[i]) && !END_ATTRIBUTENAME[character]) {
attributeName += character;
}
// if character===']' then we have an attribute without a value-definition
if (!vnode.attrs[attributeName] || ((character===']') && (vnode.attrs[attributeName]!==''))) {
return !!vnode.attrs[attributeName];
}
// now we read the value of the attribute
// however, it could be that the selector has a special `detailed` identifier set (defined by: ATTR_DETAIL_SPECIFIERS)
if (ATTR_DETAIL_SPECIFIERS[character]) {
treatment = character; // store the character to know how the attributedata should be treaded
i++; // character should be a "=" by now
}
else {
treatment = null;
}
attributeValue = '';
stringMarker = selectorItem[i+1];
attributeisString = (stringMarker==='"') || (stringMarker==="'");
attributeisString && (i++);
// end of attributaValue = (character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker))
while ((++i<len) && (character=selectorItem[i]) && !((character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker)))) {
attributeValue += character;
}
if (attributeisString) {
// if attribute is string, then we need to _remove to last stringmarker
attributeValue = attributeValue.substr(0, attributeValue.length-1);
}
else {
// if attribute is no string, then we need to typecast its value
isBoolean = ((attributeValue.length>3) && (attributeValue.length<6) &&
(checkBoolean=attributeValue.toUpperCase()) &&
((checkBoolean==='FALSE') || (checkBoolean==='TRUE')));
// typecast the value to either Boolean or Number:
attributeValue = isBoolean ? (checkBoolean==='TRUE') : parseFloat(attributeValue);
}
// depending upon how the attributedata should be treated:
if (treatment) {
switch (treatment) {
case '^': // “begins with” selector
if (!vnode.attrs[attributeName].startsWith(attributeValue)) {
return false;
}
break;
case '$': // “ends with” selector
if (!vnode.attrs[attributeName].endsWith(attributeValue)) {
return false;
}
break;
case '*': // “contains” selector (might be a substring)
if (!vnode.attrs[attributeName].contains(attributeValue)) {
return false;
}
break;
case '~': // “contains” selector as a separate word, separated by spaces
if (!(' '+vnode.attrs[attributeName]+' ').contains(' '+attributeValue+' ')) {
return false;
}
break;
case '|': // “contains” selector as a separate word, separated by `|`
if (!('|'+vnode.attrs[attributeName]+'|').contains('|'+attributeValue+'|')) {
return false;
}
break;
}
}
else if (vnode.attrs[attributeName]!==attributeValue) {
return false;
}
// we still need to increase one position:
(++i<len) && (character=selectorItem[i]);
break;
case ':':
// we have a pseudo-selector
// first, find out which one
// because '::' is a valid start (though without any selection), we start to back the next character as well:
pseudo = ':'+selectorItem[++i];
pseudoArg = '';
vParent = vnode.vParent;
vParentVChildren = vParent && vParent.vChildren;
// pseudo-selectors might have an argument passed in, like `:nth-child(2n+1)` or `:not([type="checkbox"])` --> we
// store this argument inside `pseudoArg`
// also note that combinations are possible with `:not` --> `:not(:nth-child(2n+1))`
// also note that we cannot "just" look for a closing character when running into the usage of attributes:
// for example --> `:not([data-x="some data :)"])`
// that's why -once we are inside attribute-data- we need to continue until the attribute-data ends
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
if (character==='(') {
// starting arguments
arglevel = 1;
insideAttribute = false;
insideAttributeValue = false;
while ((++i<len) && (character=selectorItem[i]) && (arglevel>0)) {
if (!insideAttribute) {
if (character==='(') {
arglevel++;
}
else if (character===')') {
arglevel--;
}
else if (character==='[') {
insideAttribute = true;
}
}
else {
// inside attribute
if (!insideAttributeValue) {
if ((character==='"') || (character==="'")) {
insideAttributeValue = true;
stringMarker = character;
}
else if (character===']') {
insideAttribute = false;
}
}
else if ((character===stringMarker) && (selectorItem[i+1]===']')) {
insideAttributeValue = false;
}
}
(arglevel>0) && (pseudoArg+=character);
}
}
else {
pseudo += character;
}
}
// now, `pseudo` is known as well as its possible pseudoArg
if (!vParentVChildren && PSEUDO_REQUIRED_CHILDREN[pseudo]) {
return false;
}
switch (pseudo) {
case ':checked': // input:checked Selects every checked <input> element
if (!vnode.attrs.checked) {
return false;
}
break;
case ':disabled': // input:disabled Selects every disabled <input> element
if (!vnode.attrs.disabled) {
return false;
}
break;
case ':empty': // p:empty Selects every <p> element that has no children (including text nodes)
if (vnode.vChildNodes && (vnode.vChildNodes.length>0)) {
return false;
}
break;
case ':enabled': // input:enabled Selects every enabled <input> element
if (vnode.attrs.disabled) {
return false;
}
break;
case PSEUDO_FIRST_CHILD: // p:first-child Selects every <p> element that is the first child of its parent
if (vParentVChildren[0]!==vnode) {
return false;
}
break;
case PSEUDO_FIRST_OF_TYPE: // p:first-of-type Selects every <p> element that is the first <p> element of its parent
for (k=vParentVChildren.indexOf(vnode)-1; k>=0; k--) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':focus': // input:focus Selects the input element which has focus
if (vnode.domNode!==DOCUMENT.activeElement) {
return false;
}
break;
case ':in-range': // input:in-range Selects input elements with a value within a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || (value<min) || (value>max)) {
return false;
}
break;
case ':lang': // p:lang(it) Selects every <p> element with a lang attribute equal to "it" (Italian)
if (vnode.attrs.lang!==pseudoArg) {
return false;
}
break;
case PSEUDO_LAST_CHILD: // p:last-child Selects every <p> element that is the last child of its parent
if (vParentVChildren[vParentVChildren.length-1]!==vnode) {
return false;
}
break;
case PSEUDO_LAST_OF_TYPE: // p:last-of-type Selects every <p> element that is the last <p> element of its parent
len2 = vParentVChildren.length;
for (k=vParentVChildren.indexOf(vnode)+1; k<len2; k++) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':not': // :not(p) Selects every element that is not a <p> element
if (vnode.matchesSelector(pseudoArg)) {
return false;
}
break;
case PSEUDO_NTH_CHILD: // p:nth-child(2) Selects every <p> element that is the second child of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
index = vParentVChildren.indexOf(vnode)+1;
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_CHILD: // p:nth-last-child(2) Selects every <p> element that is the second child of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_OF_TYPE: // p:nth-last-of-type(2) Selects every <p> element that is the second <p> element of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
index = 0;
for (k=vParentVChildren.length-1; (k>=0) && !found; k--) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_OF_TYPE: // p:nth-of-type(2) Selects every <p> element that is the second <p> element of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
len2 = vParentVChildren.length;
index = 0;
for (k=0; (k<len2) && !found; k++) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_ONLY_OF_TYPE: // p:only-of-type Selects every <p> element that is the only <p> element of its parent
len2 = vParentVChildren.length;
count = 0;
for (k=0; (k<len2) && (count<=1); k++) {
(vParentVChildren[k].tag===vnode.tag) && count++;
}
if (count!==1) {
return false;
}
break;
case PSEUDO_ONLY_CHILD: // p:only-child Selects every <p> element that is the only child of its parent
if (vParentVChildren.length!==1) {
return false;
}
break;
case ':optional': // input:optional Selects input elements with no "required" attribute
if (vnode.attrs.required) {
return false;
}
break;
case ':out-of-range': // input:out-of-range Selects input elements with a value outside a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || ((value>=min) && (value<=max))) {
return false;
}
break;
case ':read-only': // input:read-only Selects input elements with the "readonly" attribute specified
if (!vnode.attrs.readonly) {
return false;
}
break;
case ':read-write': // input:read-write Selects input elements with the "readonly" attribute NOT specified
if (vnode.attrs.readonly) {
return false;
}
break;
case ':required': // input:required Selects input elements with the "required" attribute specified
if (!vnode.attrs.required) {
return false;
}
break;
case ':root': // Selects the document's root element
if (vnode.domNode!==DOCUMENT.documentElement) {
return false;
}
break;
}
}
}
return true;
};
/**
* Splits the selector into separate subselector-items that should match different elements through the tree.
* Special characters '>' and '+' are added as separate items in the hash.
*
* @method _splitSelector
* @param selector {String} the selector-item to check the match for
* @return {Array} splitted selectors
* @protected
* @private
* @since 0.0.1
*/
_splitSelector = function(selector) {
var list = [],
len = selector.length,
sel = '',
i, character, insideDataAttr;
for (i=0; i<len; i++) {
character = selector[i];
if (character==='[') {
sel += character;
insideDataAttr = true;
}
else if (character===']') {
sel += character;
insideDataAttr = false;
}
else if (insideDataAttr || !SPLIT_CHARACTER[character]) {
sel += character;
}
else {
// unique selectoritem is found, add it to the list
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
// in case the last character was '>', '+' or '~', we need to add it as a separate item
STORABLE_SPLIT_CHARACTER[character] && (list[list.length]=character);
}
}
// add the last item
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
return list;
};
_batchEmit = function() {
// we will exactly define the 'UI:'-event --> Itags will have another emitterName
MUTATION_EVENTS.each(function (mutationEvents, vnode) {
var domNode = vnode.domNode;
if (mutationEvents[EV_REMOVED]) {
domNode.emit('UI:'+EV_REMOVED);
}
else if (mutationEvents[EV_INSERTED]) {
domNode.emit('UI:'+EV_INSERTED);
}
else {
// contentchange and attributechanges can go hand in hand
mutationEvents.each(function(value, evt) {
domNode.emit('UI:'+evt, (evt===EV_CONTENT_CHANGE) ? null : {changed: value});
});
}
});
MUTATION_EVENTS.clear();
BATCH_WILL_RUN = false;
};
_emitDestroyChildren = function(vnode) {
var children = vnode.vChildren,
len = children.length,
i, vChild;
for (i=0; i<len; i++) {
vChild = children[i];
vChild._emit(EV_REMOVED);
}
};
_markRemoved = function(vnode) {
var vChildNodes = vnode.vChildNodes,
len, i, vChildNode;
if (vnode.nodeType===1) {
Object.protectedProp(vnode, 'removedFromDOM', true);
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && _markRemoved(vChildNode);
}
}
}
};
/**
* A safe way to remove a dom-node, even if it is not present.
* Will not throw a JS error when the "to be removed" node isn't in the dom
*
* @method _tryRemoveDomNode
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param childDomNode {DOMNode} the node that needs to be removed
* @since 0.0.1
*/
_tryRemoveDomNode = function(parentDomNode, childDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && childDomNode.isItag && childDomNode.isItag()) {
DOCUMENT._itagList.remove(childDomNode);
}
try {
parentDomNode._removeChild(childDomNode);
}
catch(err) {}
};
/**
* A safe way to replace a dom-node, even if the "to be replaced"-node it is not present.
* Will not throw a JS error when the "to be replaced" node isn't in the dom. in that case, the new node will
* be appended
*
* @method _tryReplaceChild
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param newChildDomNode {DOMNode} the node that needs to be replaced
* @param oldChildDomNode {DOMNode} the node that needs to be inserted
* @since 0.0.1
*/
_tryReplaceChild = function(parentDomNode, newChildDomNode, oldChildDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && oldChildDomNode.isItag && oldChildDomNode.isItag()) {
DOCUMENT._itagList.remove(oldChildDomNode);
}
try {
parentDomNode._replaceChild(newChildDomNode, oldChildDomNode);
}
catch(err) {
// if the childDomNode isn;t there any more - for whatever reason - the we need to append the newChildNode
parentDomNode._appendChild(newChildDomNode);
}
};
vNodeProto = window._ITSAmodules.VNode = {
/**
* Check whether the vnode's domNode is equal, or contains the specified Element.
*
* @method contains
* @return {Boolean} whether the vnode's domNode is equal, or contains the specified Element.
* @since 0.0.1
*/
contains: function(otherVNode, noProtectedSearch) {
var instance = this;
if (otherVNode && (otherVNode.destroyed || (noProtectedSearch && otherVNode._systemNode))) {
return false;
}
while (otherVNode && (otherVNode!==instance)) {
otherVNode = otherVNode.vParent;
if (otherVNode && noProtectedSearch && (otherVNode._systemNode || (otherVNode.isItag && otherVNode.domNode.contentHidden))) {
return false;
}
}
return (otherVNode===instance);
},
/**
* Empties the vnode.
*
* Syncs with the dom.
*
* @method empty
* @param [full=false] {Boolean} whether system-nodes should be removed as well
* @chainable
* @since 0.0.1
*/
empty: function(full) {
delete this._scripts;
return this._setChildNodes([], null, full);
},
/**
* Returns the first child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method firstOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the first child-vnode or null when not present
* @since 0.0.1
*/
firstOfVChildren: function(cssSelector) {
var instance = this,
found, i, len, vChildren, element;
if (!cssSelector) {
return instance.vFirstElementChild;
}
vChildren = instance.vChildren;
len = vChildren.length;
for (i=0; !found && (i<len); i++) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
return found;
},
/**
* Gets the innerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* Only valid for nodetype=1 (HTMLElements)
*
* @method getHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String|undefined} the innerHTML without the elements specified, or `undefined` when not an HTMLElement
* @since 0.0.1
*/
getHTML: function(exclude, includeSystemNodes) {
var instance = this,
html, vChildNodes, len, i, vChildNode;
if (instance.nodeType===1) {
if (exclude) {
Array.isArray(exclude) || (exclude=[exclude]);
}
html = '';
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
switch (vChildNode.nodeType) {
case 1:
(exclude && exclude.contains(vChildNode.domNode)) || (!includeSystemNodes && vChildNode._systemNode) || (html+=vChildNode.getOuterHTML(exclude, includeSystemNodes));
break;
case 3:
html += vChildNode.text.replace(/</g, '<').replace(/>/g, '>');
break;
case 8:
html += '<!--' + vChildNode.text.replace(/</g, '<').replace(/>/g, '>') + '-->';
}
}
}
return html;
},
/**
* Gets the outerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* @method getOuterHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String} the outerHTML
* @since 0.0.1
*/
getOuterHTML: function(exclude, includeSystemNodes) {
var instance = this,
html,
attrs = instance.attrs;
if (instance.nodeType===1) {
if (instance.nodeType!==1) {
return instance.textContent;
}
html = '<' + instance.tag.toLowerCase();
attrs.each(function(value, key) {
html += ' '+key+'="'+value+'"';
});
instance.isVoid && (html += '/');
html += '>';
if (!instance.isVoid) {
html += ((!includeSystemNodes && instance.isItag) ? '' : instance.getHTML(exclude, includeSystemNodes)) + '</' + instance.tag.toLowerCase() + '>';
}
}
return html;
},
/**
* Checks whether the vnode has any vChildNodes (nodeType of 1, 3 or 8).
*
* @method hasVChildNodes
* @return {Boolean} whether the vnode has any vChildNodes.
* @since 0.0.1
*/
hasVChildNodes: function() {
return this.vChildNodes ? (this.vChildNodes.length>0) : false;
},
/**
* Checks whether the vnode has any vChildren (vChildNodes with nodeType of 1).
*
* @method hasVChildren
* @return {Boolean} whether the vnode has any vChildren.
* @since 0.0.1
*/
hasVChildren: function() {
return this.vChildNodes ? (this.vChildren.length>0) : false;
},
/**
* Checks whether the className is present on the vnode.
*
* @method hasClass
* @param className {String|Array} the className to check for. May be an Array of classNames, which all needs to be present.
* @return {Boolean} whether the className (or classNames) is present on the vnode
* @since 0.0.1
*/
hasClass: function(className) {
var instance = this,
check = function(cl) {
return !!instance.classNames[cl];
};
if (!instance.classNames) {
return false;
}
if (typeof className === STRING) {
return check(className);
}
else if (Array.isArray(className)) {
return className.every(check);
}
return false;
},
/**
* Returns the last child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method lastOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the last child-vnode or null when not present
* @since 0.0.1
*/
lastOfVChildren: function(cssSelector) {
var vChildren = this.vChildren,
found, i, element;
if (vChildren) {
if (!cssSelector) {
return this.vLastElementChild;
}
for (i=vChildren.length-1; !found && (i>=0); i--) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
}
return found;
},
/**
* Checks whether the vnode matches one of the specified selectors. `selectors` can be one, or multiple css-selectors,
* separated by a `comma`. For example: "#myid li.red blue" is one selector, "div.red, div.blue, div.green" are three selectors.
*
* @method matchesSelector
* @param selectors {String} one or more css-selectors
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches one of the selectors
* @since 0.0.1
*/
matchesSelector: function(selectors, relatedVNode) {
var instance = this;
if (instance.nodeType!==1) {
return false;
}
selectors = selectors.split(',');
// we can use Array.some, because there won't be many separated selectoritems,
// so the final invocation won't be delayed much compared to looping
return selectors.some(function(selector) {
return _matchesOneSelector(instance, selector, relatedVNode);
});
},
/**
* Reloads the DOM-attribute into the vnode.
*
* @method matchesSelector
* @param attributeName {String} the name of the attribute to be reloaded.
* @return {Node} the domNode that was reloaded.
* @since 0.0.1
*/
reloadAttr: function(attributeName) {
var instance = this,
domNode = instance.domNode,
attributeValue = domNode._getAttribute(attributeName),
attrs = instance.attrs,
extractStyle, extractClass;
if (instance.nodeType===1) {
attributeValue || (attributeValue='');
if (attributeValue==='') {
delete attrs[attributeName];
// in case of STYLE attributeName --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attributeName --> special treatment
(attributeName===CLASS) && (instance.classNames={});
// in case of ID attributeName --> special treatment
if ((attributeName===ID) && (instance.id)) {
delete nodeids[instance.id];
delete instance.id;
}
}
else {
attrs[attributeName] = attributeValue;
// in case of STYLE attributeName --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(attributeValue);
attributeValue = extractStyle.attrStyle;
if (attributeValue) {
attrs.style = attributeValue;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attributeName --> special treatment
extractClass = extractor.extractClass(attributeValue);
attributeValue = extractClass.attrClass;
if (attributeValue) {
attrs[CLASS] = attributeValue;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (instance.id!==attributeValue) && (delete nodeids[instance.id]);
instance.id = attributeValue;
nodeids[attributeValue] = domNode;
}
}
}
return domNode;
},
/**
* Returns the vnode's style in a serialized form: the way it appears in the dom.
*
* @method serializeStyles
* @return {String} vnode's style
* @since 0.0.1
*/
serializeStyles: function() {
return extractor.serializeStyles(this.styles);
},
/**
* Sets the vnode's and dom-nodes inner HTML.
*
* Syncs with the dom. Can be invoked multiple times without issues.
*
* @method setHTML
* @param content {String} the innerHTML
* @param [suppressItagRender] {Boolean} to suppress Itags from rendering
* @param [allowScripts=false] {Boolean} whether scripts are allowed --> these should be defined with `xscript` instead of `script`
* @chainable
* @since 0.0.1
*/
setHTML: function(content, suppressItagRender, allowScripts) {
var instance = this;
instance._setChildNodes(htmlToVNodes(content, vNodeProto, instance.ns, null, suppressItagRender, allowScripts), suppressItagRender);
return instance;
},
/**
* Syncs the vnode's nodeid (if available) inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom. Can be invoked multiple times without issues.
*
* @method storeId
* @chainable
* @since 0.0.1
*/
storeId: function() {
// store node/vnode inside WeakMap:
var instance = this;
instance.id ? (nodeids[instance.id]=instance.domNode) : (delete nodeids[instance.id]);
return instance;
},
//---- private ------------------------------------------------------------------
_addToTaglist: function() {
var instance = this,
itagList;
if (instance.isItag) {
itagList = DOCUMENT.getItags(); // also reads the dom if the list isn't build yet
instance._data || Object.protectedProp(instance, '_data', {});
if (!instance._data.ce_destroyed && !itagList.contains(instance.domNode)) {
itagList.push(instance.domNode);
}
}
},
/**
* Adds a vnode to the end of the list of vChildNodes.
*
* Syncs with the DOM.
*
* @method _appendChild
* @param VNode {vnode} vnode to append
* @private
* @return {Node} the Node that was appended
* @since 0.0.1
*/
_appendChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
size, noProccessScript, scriptContent;
if ((VNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (VNode.attrs && VNode.attrs.src) ? VNode.attrs.src : VNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
VNode._moveToParent(instance);
instance.domNode._appendChild(domNode);
if (VNode.nodeType===3) {
size = instance.vChildNodes.length;
instance._normalize();
// if the size changed, then the domNode was merged
(size===instance.vChildNodes.length) || (domNode=instance.vChildNodes[instance.vChildNodes.length-1].domNode);
}
if (VNode.nodeType===1) {
VNode._addToTaglist();
VNode._emit(EV_INSERTED);
}
return domNode;
}
},
/**
* Removes the vnode from its parent vChildNodes- and vChildren-list.
*
* Does NOT sync with the dom.
*
* @method _deleteFromParent
* @private
* @chainable
* @since 0.0.1
*/
_deleteFromParent: function() {
var instance = this,
vParent = instance.vParent;
if (vParent && vParent.vChildNodes) {
vParent.vChildNodes.remove(instance);
// force to recalculate the vChildren on a next call:
(instance.nodeType===1) && (vParent._vChildren=null);
}
return instance;
},
/**
* Cleans up (empties) vnode's `_data`
*
* @method _cleanData
* @private
* @chainable
* @since 0.0.1
*/
_cleanData: function() {
var instance = this,
data = instance._data;
data && data.each(
function(value, key) {
delete data[key];
}
);
return instance;
},
/**
* Cleans up (removes) duplicated `style` definitions.
*
* @method _cleanupStyle
* @private
* @chainable
* @since 0.0.1
*/
_cleanupStyle: function() {
var instance = this,
compare = [],
removal = [],
vChildren = instance.vChildren,
len = vChildren.length,
vChild, i, styleContent, vChildInner;
for (i=0; i<len; i++) {
vChild = vChildren[i];
if (vChild.tag==='STYLE') {
vChildInner = vChild.vChildNodes[0];
if (vChildInner) {
styleContent = vChildInner.text;
if (compare.contains(styleContent)) {
removal[removal.length] = vChild;
}
else {
compare[compare.length] = styleContent;
}
}
else {
// empty style-tag
removal[removal.length] = vChild;
}
}
}
removal.forEach(function(vnode) {
instance._removeChild(vnode);
});
},
/**
* Destroys the vnode and all its vnode-vChildNodes.
* Removes it from its vParent.vChildNodes list,
* also removes its definitions inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom.
*
* @method _destroy
* @private
* @chainable
* @since 0.0.1
*/
_destroy: function(silent) {
var instance = this,
vChildNodes = instance.vChildNodes,
len, i, vChildNode, vParent, treeNodes;
if (!instance.destroyed) {
if (!silent) {
// Because we don't want to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
later(function() {
instance._emit(EV_REMOVED, null, null, null, true);
}, 5);
}
Object.protectedProp(instance, 'destroyed', true);
// first: determine the dom-tree, which module `event-dom` needs to determine where the node was before it was destroyed:
treeNodes = [instance];
vParent = instance.vParent;
while (vParent) {
treeNodes[treeNodes.length] = vParent;
vParent = vParent.vParent;
}
// mark all its vChildNodes so we can see if the node is in the DOM
_markRemoved(instance);
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && instance.isItag) {
DOCUMENT._itagList.remove(instance.domNode);
}
instance._scripts && (instance._scripts.length=0);
// The definite cleanup needs to be done after a timeout:
// someone might need to handle the Element when removed (fe to cleanup specific things)
later(function() {
instance._cleanData();
if (instance.nodeType===1) {
// _destroy all its vChildNodes
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && vChildNode._destroy(true);
}
}
}
instance._vChildren = null;
// explicitely set instance.domNode._vnode and instance.domNode to null in order to prevent problems with the GC (we break the circular reference)
delete instance.domNode._vnode;
// if valid id, then _remove the DOMnodeRef from internal hash
instance.id && delete nodeids[instance.id];
}, silent ? 0 : DESTROY_DELAY);
instance._deleteFromParent();
// Do not make domNode `null` --> it could be used even when not in the dom
}
return instance;
},
_emit: function(evt, attribute, newValue, prevValue, destroyEvt) {
/**
* Emitted by every Element that gets inserted.
*
* @event nodeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets removed.
*
* @event noderemove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets its content changed (innerHTML/innerText).
*
* @event nodecontentchange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute inserted.
*
* @event attributeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* </ul>
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute removed.
*
* @event attributeremove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Strings of the attributeNames that are removed
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute changed.
*
* @event attributechange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* <li>prevValue</li>
* </ul>
* @since 0.1
*/
var instance = this,
silent, attrMutations, mutationEvents, mutation, vParent;
if (!DOCUMENT.hasMutationSubs || (instance.nodeType!==1)) {
return;
}
silent = !!DOCUMENT._suppressMutationEvents;
if (!silent && (destroyEvt || !instance.destroyed)) {
// Because we don't wannt to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
mutationEvents = MUTATION_EVENTS.get(instance) || {};
if (attribute) {
attrMutations = mutationEvents[evt] || [];
if (evt===EV_ATTRIBUTE_REMOVED) {
mutation = attribute;
}
else {
mutation = {
attribute: attribute
};
if ((evt===EV_ATTRIBUTE_INSERTED) || (evt===EV_ATTRIBUTE_CHANGED)) {
mutation.newValue = newValue;
}
if ((evt===EV_ATTRIBUTE_CHANGED) && prevValue) {
mutation.prevValue = prevValue;
}
}
attrMutations.push(mutation);
mutationEvents[evt] = attrMutations;
}
else {
mutationEvents[evt] = true;
}
MUTATION_EVENTS.set(instance, mutationEvents);
// now set all parent to have a nodecontentchange:
vParent = instance.vParent;
vParent && vParent._emit(EV_CONTENT_CHANGE);
// in case of removal we need to emit EV_REMOVED for all children right now
// for they will be actually removed silently after a delay of 1 minute
(evt===EV_REMOVED) && _emitDestroyChildren(instance);
if (!BATCH_WILL_RUN) {
BATCH_WILL_RUN = true;
async(function() {
_batchEmit();
});
}
}
return instance;
},
/**
* Inserts `newVNode` before `refVNode`.
*
* Syncs with the DOM.
*
* @method _insertBefore
* @param newVNode {vnode} vnode to insert
* @param refVNode {vnode} The vnode before which newVNode should be inserted.
* @private
* @return {Node} the Node being inserted (equals domNode)
* @since 0.0.1
*/
_insertBefore: function(newVNode, refVNode) {
var instance = this,
domNode = newVNode.domNode,
index = instance.vChildNodes.indexOf(refVNode),
noProccessScript, scriptContent;
if ((newVNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (newVNode.attrs && newVNode.attrs.src) ? newVNode.attrs.src : newVNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
if (index!==-1) {
newVNode._moveToParent(instance, index);
instance.domNode._insertBefore(domNode, refVNode.domNode);
(newVNode.nodeType===3) && instance._normalize();
if (newVNode.nodeType===1) {
newVNode._addToTaglist();
newVNode._emit(EV_INSERTED);
}
return domNode;
}
else {
console.warn('trying to insert before, but no ref-node found. Will append the new node');
return instance._appendChild(newVNode);
}
}
},
/**
* Moves the vnode from its current parent.vChildNodes list towards a new parent vnode at the specified position.
*
* Does NOT sync with the dom.
*
* @method _moveToParent
* @param parentVNode {vnode} the parent-vnode
* @param [index] {Number} the position of the child. When not specified, it will be appended.
* @private
* @chainable
* @since 0.0.1
*/
_moveToParent: function(parentVNode, index) {
var instance = this,
vParent = instance.vParent;
instance._deleteFromParent();
instance.vParent = parentVNode;
parentVNode.vChildNodes || (parentVNode.vChildNodes=[]);
(typeof index==='number') ? parentVNode.vChildNodes.insertAt(instance, index) : (parentVNode.vChildNodes[parentVNode.vChildNodes.length]=instance);
// force to recalculate the vChildren on a next call:
vParent && (instance.nodeType===1) && (vParent._vChildren = null);
// force to recalculate the vChildren on a next call:
parentVNode && (instance.nodeType===1) && (parentVNode._vChildren=null);
return instance;
},
/**
* Removes empty TextNodes and merges following TextNodes inside the vnode.
*
* Syncs with the dom.
*
* @method _normalize
* @private
* @chainable
* @since 0.0.1
*/
_normalize: function() {
var instance = this,
domNode = instance.domNode,
vChildNodes = instance.vChildNodes,
changed = false,
i, preChildNode, vChildNode;
if (!instance._unNormalizable && vChildNodes) {
for (i=vChildNodes.length-1; i>=0; i--) {
vChildNode = vChildNodes[i];
preChildNode = vChildNodes[i-1]; // i will get the value `-1` eventually, which leads into undefined preChildNode
if (vChildNode.nodeType===3) {
if (vChildNode.text==='') {
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
else if (preChildNode && preChildNode.nodeType===3) {
preChildNode.text += vChildNode.text;
preChildNode.domNode.nodeValue = unescapeEntities(preChildNode.text);
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
}
}
}
changed && instance._emit(EV_CONTENT_CHANGE);
return instance;
},
/**
* Makes the vnode `normalizable`. Could be set to `false` when batch-inserting nodes, while `normalizaing` manually at the end.
* Afterwards, you should always reset `normalizable` to true.
*
* @method _normalizable
* @param value {Boolean} whether the vnode should be normalisable.
* @private
* @chainable
* @since 0.0.1
*/
_normalizable: function(value) {
var instance = this;
value ? (delete instance._unNormalizable) : (instance._unNormalizable=true);
return instance;
},
/**
* Prevents MutationObserver from making the dom sync with the vnode.
* Should be used when manipulating the dom from within the vnode itself (to preventing looping)
*
* @method _noSync
* @chainable
* @private
* @since 0.0.1
*/
_noSync: function() {
var instance = this;
if (!instance._nosync) {
instance._nosync = true;
async(function() {
instance._nosync = false;
});
}
return instance;
},
/**
* Removes the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _removeAttr
* @param attributeName {String}
* @private
* @chainable
* @since 0.0.1
*/
_removeAttr: function(attributeName, suppressItagRender) {
var instance = this,
attributeNameSplitted, ns;
if (instance.isItag && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
console.warn('Not allowed to remove the attribute '+attributeName);
return instance;
}
if (instance.attrs[attributeName]!==undefined) {
delete instance.attrs[attributeName];
// in case of STYLE attribute --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attribute --> special treatment
(attributeName===CLASS) && (instance.classNames={});
if (attributeName===ID) {
delete nodeids[instance.id];
delete instance.id;
}
instance._emit(EV_ATTRIBUTE_REMOVED, attributeName);
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
instance.domNode._removeAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName);
}
else {
instance.domNode._removeAttribute(attributeName);
}
}
return instance;
},
/**
* Removes the vnode's child-vnode from its vChildren and the DOM.
*
* Syncs with the DOM.
*
* @method removeChild
* @param VNode {vnode} the child-vnode to remove
* @private
* @since 0.0.1
*/
_removeChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
hadFocus = domNode && domNode.hasFocus && domNode.hasFocus() && (VNode.attrs['fm-lastitem']==='true'),
parentVNode = VNode.vParent;
VNode._destroy();
_tryRemoveDomNode(instance.domNode, VNode.domNode);
instance._normalize();
// now, reset the focus on focusmanager when needed:
if (hadFocus) {
while (parentVNode && !parentVNode.attrs['fm-manage']) {
parentVNode = parentVNode.vParent;
}
parentVNode && parentVNode.domNode.focus();
}
},
/**
* Replaces the current vnode at the parent.vChildNode list by `newVNode`
*
* Does NOT sync with the dom.
*
* @method _replaceAtParent
* @param newVNode {Object} the new vnode which should take over the place of the current vnode
* @private
* @chainable
* @since 0.0.1
*/
_replaceAtParent: function(newVNode) {
var instance = this,
vParent = instance.vParent,
vChildNodes, index;
if (vParent && (vChildNodes=vParent.vChildNodes)) {
index = vChildNodes.indexOf(instance);
// force to recalculate the vChildren on a next call:
((instance.nodeType===1) || (newVNode.nodeType===1)) && (instance.vParent._vChildren=null);
vChildNodes[index] = newVNode;
}
return instance._destroy();
},
/**
* Sets the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _setAttr
* @param attributeName {String}
* @param value {String} the value for the attributeName
* @param [force=false] {Boolean} force the attribute to be set, even if restrictions would deny it
* @private
* @chainable
* @since 0.0.1
*/
_setAttr: function(attributeName, value, force, suppressItagRender) {
var instance = this,
extractStyle, extractClass,
attrs = instance.attrs,
prevVal = attrs[attributeName],
domNode = instance.domNode,
attributeNameSplitted, ns;
if (instance.isItag && !force && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
if (prevVal!=value) {
console.warn('Not allowed to set the attribute '+attributeName);
}
return instance;
}
// don't check by !== --> value isn't parsed into a String yet
if (prevVal && ((value===undefined) || (value===null))) {
instance._removeAttr(attributeName, suppressItagRender);
return instance;
}
// attribute-values are always Strings:
value = String(value);
// attribute-values will be stored without " or '
value = value.replace(/"/g, '"').replace(/'/g, "'");
if (prevVal!=value) {
attrs[attributeName] = value;
// in case of STYLE attribute --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(value);
value = extractStyle.attrStyle;
if (value) {
attrs.style = value;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attribute --> special treatment
extractClass = extractor.extractClass(value);
value = extractClass.attrClass;
if (value) {
attrs[CLASS] = value;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (delete nodeids[instance.id]);
instance.id = value;
nodeids[value] = domNode;
}
instance._emit(prevVal ? EV_ATTRIBUTE_CHANGED : EV_ATTRIBUTE_INSERTED, attributeName, value, prevVal);
// when set in the dom --> quotes need to be set as "
value = value.replace(/"/g, '"');
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
domNode._setAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName, value);
}
else {
domNode._setAttribute(attributeName, value);
}
}
return instance;
},
/**
* Redefines the attributes of both the vnode as well as its related dom-node. The new
* definition replaces any previous attributes (without touching unmodified attributes).
* the `is` attribute cannot be changed for itags.
*
* Syncs the new vnode's attributes with the dom.
*
* @method _setAttrs
* @param newAttrs {Object|Array} the new attributes to be set
* @private
* @chainable
* @since 0.0.1
*/
_setAttrs: function(newAttrs, suppressItagRender) {
// does sync the DOM
var instance = this,
attrsObj, attr, attrs, i, key, keys, len, value;
if (instance.nodeType!==1) {
return;
}
instance._noSync();
attrs = instance.attrs;
attrs.id && (delete nodeids[attrs.id]);
if (Object.isObject(newAttrs)) {
attrsObj = newAttrs;
}
else {
attrsObj = {};
len = newAttrs.length;
for (i=0; i<len; i++) {
attr = newAttrs[i];
attrsObj[attr.name] = attr.value;
}
}
if (instance.isItag && !suppressItagRender) {
if (attrs.is) {
attrsObj.is = attrs.is;
}
else {
delete attrsObj.is;
delete attrsObj.Is;
delete attrsObj.iS;
delete attrsObj.IS;
}
}
// first _remove the attributes that are no longer needed.
// quickest way for object iteration: http://jsperf.com/object-keys-iteration/20
keys = Object.keys(attrs);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
attrsObj[key] || instance._removeAttr(key, suppressItagRender);
}
// next: every attribute that differs: redefine
keys = Object.keys(attrsObj);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
value = attrsObj[key];
(attrs[key]===value) || instance._setAttr(key, value, suppressItagRender);
}
return instance;
},
/**
* Redefines the childNodes of both the vnode as well as its related dom-node. The new
* definition replaces any previous nodes. (without touching unmodified nodes).
*
* Syncs the new vnode's childNodes with the dom.
*
* @method _setChildNodes
* @param newVChildNodes {Array} array with vnodes which represent the new childNodes
* @private
* @chainable
* @since 0.0.1
*/
_setChildNodes: function(newVChildNodes, suppressItagRender, removeSystemElements) {
// does sync the DOM
var instance = this,
vChildNodes = instance.vChildNodes || [],
domNode = instance.domNode,
forRemoval = [],
i, oldChild, newChild, newLength, len, len2, childDomNode, nodeswitch, bkpAttrs, cleanupStyle,
process, bkpChildNodes, needNormalize, prevSuppress, scriptContent, _scripts, scriptLen, j;
instance._noSync();
// first: reset ._vChildren --> by making it empty, its getter will refresh its list on a next call
instance._vChildren = null;
// if newVChildNodes is undefined, then we assume it to be empty --> an empty array
newVChildNodes || (newVChildNodes=[]);
// quickest way to loop through array is by using for loops: http://jsperf.com/array-foreach-vs-for-loop/5
len = vChildNodes.length;
// we need to add the systemNodes -if any- to the new newVChildNodes: they should be retained.
// SystemNodes are always places at the beginning, so they won't get reshuffled:
if (!removeSystemElements) {
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
if (oldChild._systemNode) {
newVChildNodes.insertAt(oldChild, i);
}
else {
break;
}
}
}
newLength = newVChildNodes.length;
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
childDomNode = oldChild.domNode;
if (i < newLength) {
newChild = newVChildNodes[i];
newChild.vParent || (newChild.vParent=instance);
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
/*jshint boss:true */
switch (nodeswitch=NODESWITCH[oldChild.nodeType][newChild.nodeType]) {
/*jshint boss:false */
case 1: // oldNodeType==Element, newNodeType==Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
if ((oldChild.tag!==newChild.tag) ||
((oldChild.tag===newChild.tag) && oldChild.isItag && (oldChild.attrs.is!==newChild.attrs.is))) {
// new tag --> completely replace
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
/*jshint proto:true */
oldChild.isItag && oldChild.domNode.destroyUI && oldChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
newChild.attrs = {}; // reset to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
oldChild._replaceAtParent(newChild);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
else {
// same tag --> only update what is needed
// NOTE: when this._unchangableAttrs exists, an itag-element syncs its UI -->
if (oldChild._data) {
// we might need to set the class `itag-rendered` when the attributeData says so:
// this happens when an itag gets refreshed with an unrendered definition
if (oldChild._data.itagRendered && !newChild.hasClass('itag-rendered')) {
newChild.classNames['itag-rendered'] = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' '+'itag-rendered';
}
else {
newChild.attrs[CLASS] = 'itag-rendered';
}
}
// we might need to set the class `focussed` when the attributeData says so:
// this happens when an itag gets rerendered: its renderFn doesn't know if any elements
// were focussed
if (oldChild._data.focussed && !newChild.hasClass('focussed')) {
newChild.classNames.focussed = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' ' + 'focussed';
}
else {
newChild.attrs[CLASS] = 'focussed';
}
}
if (oldChild._data['fm-tabindex']) {
// node has the tabindex set by the focusmanager,
// but that info might got lost with re-rendering of the new element
newChild.attrs.tabindex = '0';
}
}
if (oldChild.isItag) {
prevSuppress = DOCUMENT._suppressMutationEvents || false;
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
/*jshint proto:true */
newChild.domNode.destroyUI && newChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
oldChild._setAttrs(newChild.attrs, suppressItagRender);
newChild._destroy(true); // destroy through the vnode and removing from DOCUMENT._itagList
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(prevSuppress);
}
else {
oldChild._setAttrs(newChild.attrs, suppressItagRender);
// next: sync the vChildNodes:
oldChild._setChildNodes(newChild.vChildNodes, suppressItagRender);
}
// reset ref. to the domNode, for it might have been changed by newChild:
oldChild.id && (nodeids[oldChild.id]=childDomNode);
newVChildNodes[i] = oldChild;
}
}
break;
case 2: // oldNodeType==Element, newNodeType==TextNode
// case2 and case3 should be treated the same
case 3: // oldNodeType==Element, newNodeType==Comment
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
oldChild._replaceAtParent(newChild);
instance._emit(EV_CONTENT_CHANGE);
break;
case 4: // oldNodeType==TextNode, newNodeType==Element
// case4 and case7 should be treated the same
case 7: // oldNodeType==Comment, newNodeType==Element
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
// oldChild.isVoid = newChild.isVoid;
// delete oldChild.text;
instance._emit(EV_CONTENT_CHANGE);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
break;
case 5: // oldNodeType==TextNode, newNodeType==TextNode
// case5 and case9 should be treated the same
case 9: // oldNodeType==Comment, newNodeType==Comment
if (oldChild.text!==newChild.text) {
oldChild.text = newChild.text;
oldChild.domNode.nodeValue = unescapeEntities(newChild.text);
instance._emit(EV_CONTENT_CHANGE);
}
newVChildNodes[i] = oldChild;
break;
case 6: // oldNodeType==TextNode, newNodeType==Comment
// case6 and case8 should be treated the same
case 8: // oldNodeType==Comment, newNodeType==TextNode
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = oldChild.vParent;
instance._emit(EV_CONTENT_CHANGE);
}
if ((nodeswitch===2) || (nodeswitch===5) || (nodeswitch===8)) {
needNormalize = true;
}
}
else {
// _remove previous definition
_tryRemoveDomNode(domNode, oldChild.domNode);
// the oldChild needs to be removed, however, this cannot be done right now, for it would effect the loop
// so we store it inside a hash to remove it later
forRemoval[forRemoval.length] = oldChild;
}
}
// now definitely remove marked childNodes:
len2 = forRemoval.length;
for (i=0; i<len2; i++) {
forRemoval[i]._destroy();
}
// now we add all new vChildNodes that go beyond `len`:
for (i = len; i < newLength; i++) {
newChild = newVChildNodes[i];
newChild.vParent = instance;
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
switch (newChild.nodeType) {
case 1: // Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
domNode._appendChild(newChild.domNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
break;
case 3: // TextNode
needNormalize = true;
// we need to break through --> no `break`
/* falls through */
default: // TextNode or CommentNode
// newChild.domNode.nodeValue = newChild.text;
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
domNode._appendChild(newChild.domNode);
instance._emit(EV_CONTENT_CHANGE);
}
newChild.storeId();
}
instance.vChildNodes = newVChildNodes;
needNormalize && instance._normalize();
cleanupStyle && instance._cleanupStyle();
return instance;
},
_setUnchangableAttrs: function(unchangableObj) {
this._unchangableAttrs = unchangableObj;
}
};
//---- properties ------------------------------------------------------------------
/**
* A hash of all the `attributes` of the vnode's representing dom-node.
*
* @property attrs
* @type Object
* @since 0.0.1
*/
/**
* Hash with all the classes of the vnode. Every class represents a key, all values are set `true`.
*
* @property classNames
* @type Object
* @since 0.0.1
*/
/**
* The `id` of the vnode's representing dom-node (if any).
*
* @property id
* @type String
* @since 0.0.1
*/
/**
* Tells whether tag is a void Element. Examples are: `br`, `img` and `input`. Non-void Elements are f.e. `div` and `table`.
* For TextNodes and CommentNodes, this property is `undefined`.
*
* @property isVoid
* @type Boolean
* @since 0.0.1
*/
/**
* The `nodeType` of the vnode's representing dom-node (1===ElementNode, 3===TextNode, 8===CommentNode).
*
* @property nodeType
* @type Number
* @since 0.0.1
*/
/**
* The `tag` of the vnode's representing dom-node (allways uppercase).
*
* @property tag
* @type String
* @since 0.0.1
*/
/**
* The `content` of the vnode's representing dom-node, in case it is a TextNode or CommentNode.
* Equals dom-node.nodeValue.
*
* Is `undefined` for ElementNodes.
*
* @property text
* @type String
* @since 0.0.1
*/
/**
* Hash with all the childNodes (vnodes). vChildNodes are any kind of vnodes (nodeType===1, 3 or 8)
*
* @property vChildNodes
* @type Array
* @since 0.0.1
*/
/**
* The underlying `dom-node` that the vnode represents.
*
* @property domNode
* @type domNode
* @since 0.0.1
*/
/**
* vnode's parentNode (defined as a vnode itself).
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
Object.defineProperties(vNodeProto, {
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property innerHTML
* @type String
* @since 0.0.1
*/
innerHTML: {
get: function() {
return this.getHTML();
},
set: function(v) {
this.setHTML(v);
}
},
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property nodeValue
* @type String
* @since 0.0.1
*/
nodeValue: {
get: function() {
var instance = this;
return ((instance.nodeType===3) || (instance.nodeType===8)) ? instance.text : null;
},
set: function(v) {
var instance = this,
newTextContent, prevTextContent;
if ((instance.nodeType===3) || (instance.nodeType===8)) {
prevTextContent = instance.domNode.textContent;
instance.domNode.textContent = v;
// set .text AFTER the dom-node is updated --> the content might be escaped!
newTextContent = instance.text = instance.domNode.textContent;
(newTextContent!==prevTextContent) && instance._emit(EV_CONTENT_CHANGE);
}
}
},
/**
* Gets or sets the outerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property outerHTML
* @type String
* @since 0.0.1
*/
outerHTML: {
get: function() {
return this.getOuterHTML();
},
set: function(v) {
var instance = this,
vParent = instance.vParent,
id = instance.attrs.id,
vnode, vnodes, bkpAttrs, bkpChildNodes, i, len, vChildNodes, isLastChildNode, index, refDomNode;
if ((instance.nodeType!==1) || !vParent) {
return;
}
instance._noSync();
vChildNodes = vParent.vChildNodes;
index = vChildNodes.indexOf(instance);
isLastChildNode = (index===(vChildNodes.length-1));
isLastChildNode || (refDomNode=vChildNodes[index+1].domNode);
vnodes = htmlToVNodes(v, vNodeProto, vParent.ns, vParent);
len = vnodes.length;
if (len>0) {
// the first vnode will replace the current instance:
vnode = vnodes[0];
if (vnode.nodeType===1) {
if (vnode.tag!==instance.tag) {
// new tag --> completely replace
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
id && (delete nodeids[id]);
vnode.attrs = {}; // reset to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
// vnode.attrs = bkpAttrs;
// vnode.vChildNodes = bkpChildNodes;
vnode.id && (nodeids[vnode.id]=vnode.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
else {
instance._setAttrs(vnode.attrs);
instance._setChildNodes(vnode.vChildNodes);
}
}
else {
id && (delete nodeids[id]);
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
}
for (i=1; i<len; i++) {
vnode = vnodes[i];
switch (vnode.nodeType) {
case 1: // Element
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
vnode.attrs = {}; // reset, to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._insertBefore(vnode.domNode, refDomNode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
break;
default: // TextNode or CommentNode
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._appendChild(vnode.domNode, refDomNode);
}
vnode.storeId();
vnode._moveToParent(vParent, index+i);
}
}
},
/**
* Gets or sets the innerContent of the Node as plain text.
*
* The setter syncs with the DOM.
*
* @property textContent
* @type String
* @since 0.0.1
*/
textContent: {
get: function() {
var instance = this,
text = '',
vChildNodes = instance.vChildNodes,
len, i, vChildNode;
if (instance.nodeType===1) {
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
text += (vChildNode.nodeType===3) ? vChildNode.text : ((vChildNode.nodeType===1) ? vChildNode.textContent : '');
}
}
else {
text = instance.text;
}
return text;
},
set: function(v) {
var vnode = Object.create(vNodeProto);
vnode.domNode = DOCUMENT.createTextNode(v);
// create circular reference:
vnode.domNode._vnode = vnode;
vnode.nodeType = 3;
vnode.text = vnode.domNode.textContent;
this._setChildNodes([vnode]);
}
},
/**
* Hash with all the children (vnodes). vChildren are vnodes that have a representing dom-node that is an HtmlElement (nodeType===1)
*
* @property vChildren
* @type Array
* @since 0.0.1
*/
vChildren: {
get: function() {
var instance = this,
children = instance._vChildren,
vChildNode, vChildNodes, i, len;
vChildNodes = instance.vChildNodes;
if (vChildNodes && !children) {
children = instance._vChildren = [];
len = vChildNodes.length;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
(vChildNode.nodeType===1) && (children[children.length]=vChildNode);
}
}
children || (children = instance._vChildren = []);
return children;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirst
* @type vnode
* @since 0.0.1
*/
vFirst: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstChild;
}
},
/**
* Reference to the first vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirstChild
* @type vnode
* @since 0.0.1
*/
vFirstChild: {
get: function() {
return (this.vChildNodes && this.vChildNodes[0]) || null;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vFirstElement
* @type vnode
* @since 0.0.1
*/
vFirstElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstElementChild;
}
},
/**
* Reference to the first vChild, where the related dom-node an Element (nodeType===1).
*
* @property vFirstElementChild
* @type vnode
* @since 0.0.1
*/
vFirstElementChild: {
get: function() {
return this.vChildren[0] || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLast
* @type vnode
* @since 0.0.1
*/
vLast: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastChild;
}
},
/**
* Reference to the last vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLastChild
* @type vnode
* @since 0.0.1
*/
vLastChild: {
get: function() {
var vChildNodes = this.vChildNodes;
return (vChildNodes && vChildNodes[vChildNodes.length-1]) || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vLastElement
* @type vnode
* @since 0.0.1
*/
vLastElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastElementChild;
}
},
/**
* Reference to the last vChild, where the related dom-node an Element (nodeType===1).
*
* @property vLastElementChild
* @type vnode
* @since 0.0.1
*/
vLastElementChild: {
get: function() {
var vChildren = this.vChildren;
return vChildren[vChildren.length-1] || null;
}
},
/**
* the Parent vnode
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
/**
* Reference to the next of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vNext
* @type vnode
* @since 0.0.1
*/
vNext: {
get: function() {
return _findNodeSibling(this, true);
}
},
/**
* Reference to the next of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vNextElement
* @type vnode
* @since 0.0.1
*/
vNextElement: {
get: function() {
return _findElementSibling(this, true);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vPrevious
* @type vnode
* @since 0.0.1
*/
vPrevious: {
get: function() {
return _findNodeSibling(this);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vPreviousElement
* @type vnode
* @since 0.0.1
*/
vPreviousElement: {
get: function() {
return _findElementSibling(this);
}
}
});
return vNodeProto;
};
},{"./attribute-extractor.js":3831,"./html-parser.js":3835,"./vdom-ns.js":3837,"js-ext/extra/hashmap.js":3789,"js-ext/extra/lightmap.js":3790,"js-ext/lib/array.js":3791,"js-ext/lib/object.js":3792,"js-ext/lib/string.js":3794,"polyfill":3811,"utils/lib/timers.js":3814}],3839:[function(require,module,exports){
arguments[4][100][0].apply(exports,arguments)
},{"./partials/extend-document.js":3833,"./partials/extend-element.js":3834,"./partials/node-parser.js":3836,"js-ext/extra/hashmap.js":3789,"js-ext/lib/object.js":3792,"utils/lib/timers.js":3814}],3840:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":3841}],3841:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":3842,"js-ext/lib/object.js":3843}],3842:[function(require,module,exports){
module.exports=require(4)
},{}],3843:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3842,"polyfill/polyfill-base.js":3846,"utils":3847}],3844:[function(require,module,exports){
module.exports=require(14)
},{}],3845:[function(require,module,exports){
module.exports=require(15)
},{}],3846:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3844,"./lib/window.console.js":3845}],3847:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3848,"./lib/timers.js":3849}],3848:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3842,"polyfill/polyfill-base.js":3852}],3849:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3852}],3850:[function(require,module,exports){
module.exports=require(14)
},{}],3851:[function(require,module,exports){
module.exports=require(15)
},{}],3852:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3850,"./lib/window.console.js":3851}],3853:[function(require,module,exports){
arguments[4][634][0].apply(exports,arguments)
},{"./lib/hammer-2.0.4.js":3854,"event-dom":3855}],3854:[function(require,module,exports){
module.exports=require(635)
},{"utils":3950}],3855:[function(require,module,exports){
"use strict";
/**
* Integrates DOM-events to event. more about DOM-events:
* http://www.smashingmagazine.com/2013/11/12/an-introduction-to-dom-events/
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @example
* Event = require('event-dom')(window);
*
* @module event
* @submodule event-dom
* @class Event
* @since 0.0.1
*/
var NAME = '[event-dom]: ',
Event = require('event'),
later = require('utils').later,
createHashMap = require('js-ext/extra/hashmap.js').createMap,
OUTSIDE = 'outside',
REGEXP_NODE_ID = /^#\S+$/,
REGEXP_EXTRACT_NODE_ID = /#(\S+)/,
REGEXP_UI_OUTSIDE = /^.+outside$/,
TIME_BTN_PRESSED = 200,
PURE_BUTTON_ACTIVE = 'pure-button-active',
UI = 'UI:',
NODE = 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
TAP = 'tap',
CLICK = 'click',
RIGHTCLICK = 'right'+CLICK,
CENTERCLICK = 'center'+CLICK,
EV_REMOVED = UI+NODE+REMOVE,
EV_INSERTED = UI+NODE+INSERT,
EV_CONTENT_CHANGE = UI+NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = UI+ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = UI+ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = UI+ATTRIBUTE+INSERT,
mutationEventsDefined = false,
NO_DEEP_SEARCH = {},
/*
* Internal hash containing all DOM-events that are listened for (at `document`).
*
* DOMEvents = {
* 'tap': callbackFn,
* 'mousemove': callbackFn,
* 'keypress': callbackFn
* }
*
* @property DOMEvents
* @default {}
* @type Object
* @private
* @since 0.0.1
*/
DOMEvents = {};
require('js-ext/lib/string.js');
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('polyfill/polyfill-base.js');
module.exports = function (window) {
var DOCUMENT = window.document,
_domSelToFunc, _evCallback, _findCurrentTargets, _preProcessor, _setupEvents, _setupMutationListener, _teardownMutationListener,
_setupDomListener, _teardownDomListener, SORT, _sortFunc, _sortFuncReversed, _getSubscribers, _selToFunc, MUTATION_EVENTS, preventClick;
require('vdom')(window);
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.EventDom) {
return Event; // Event was already extended
}
console.warn('event-dom 15');
MUTATION_EVENTS = [EV_REMOVED, EV_INSERTED, EV_CONTENT_CHANGE, EV_ATTRIBUTE_REMOVED, , EV_ATTRIBUTE_CHANGED, EV_ATTRIBUTE_INSERTED];
/*
* Transforms the selector to a valid function
*
* @method _selToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_selToFunc = function(customEvent, subscriber) {
Event._sellist.some(function(selFn) {
return selFn(customEvent, subscriber);
});
};
/*
* Creates a filterfunction out of a css-selector. To be used for catching any dom-element, without restrictions
* of any context (like Parcels can --> Parcel.Event uses _parcelSelToDom instead)
* On "non-outside" events, subscriber.t is set to the node that first matches the selector
* so it can be used to set as e.target in the final subscriber
*
* @method _domSelToFunc
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_domSelToFunc = function(customEvent, subscriber) {
// this stage is runned during subscription
var outsideEvent = REGEXP_UI_OUTSIDE.test(customEvent),
selector = subscriber.f,
context = subscriber.o,
vnode = subscriber.o.vnode,
isCustomElement = vnode && vnode.isItag,
visibleContent = isCustomElement && !vnode.domNode.contentHidden,
nodeid, byExactId, newTarget, deepSearch;
isCustomElement = false;
console.log(NAME, '_domSelToFunc type of selector = '+typeof selector);
// note: selector could still be a function: in case another subscriber
// already changed it.
if ((!selector && !isCustomElement) || (typeof selector === 'function')) {
subscriber.n || (subscriber.n = isCustomElement ? context : DOCUMENT);
return true;
}
selector || (selector='');
nodeid = selector.match(REGEXP_EXTRACT_NODE_ID);
nodeid ? (subscriber.nId=nodeid[1]) : (subscriber.n=isCustomElement ? context : DOCUMENT);
byExactId = REGEXP_NODE_ID.test(selector);
deepSearch = !NO_DEEP_SEARCH[customEvent];
// set the selector to `subscriber._s` so that e.currentTarget can calculate it:
subscriber._s = selector;
subscriber.f = function(e) {
// this stage is runned when the event happens
console.log(NAME, '_domSelToFunc inside filter. selector: '+selector);
var node = e.target,
vnode = node.vnode,
character1 = selector && selector.substr(1),
match = false;
if (!isCustomElement || visibleContent || context.contains(node)) {
if (selector==='') {
match = true;
}
else {
// e.target is the most deeply node in the dom-tree that caught the event
// our listener uses `selector` which might be a node higher up the tree.
// we will reset e.target to this node (if there is a match)
// note that e.currentTarget will always be `document` --> we're not interested in that
// also, we don't check for `node`, but for node.matchesSelector: the highest level `document`
// is not null, yet it doesn't have .matchesSelector so it would fail
// we DON'T want this for the focus and blur event!
if (deepSearch) {
if (vnode) {
// we go through the vdom
if (!vnode.removedFromDOM) {
while (vnode && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = vnode.domNode;
}
vnode = vnode.vParent;
}
}
}
else {
// we go through the dom
while (node.matchesSelector && !match) {
console.log(NAME, '_domSelToFunc inside filter check match using the dom');
match = byExactId ? (node.id===character1) : node.matchesSelector(selector);
// if there is a match, then set
// e.target to the target that matches the selector
if (match && !outsideEvent) {
subscriber.t = node;
}
node = node.parentNode;
}
}
}
else {
console.log(NAME, '_domSelToFunc inside filter check match using the vdom');
if (vnode) {
match = byExactId ? (vnode.id===character1) : vnode.matchesSelector(selector);
}
else {
match = node.matchesSelector && (byExactId ? (node.id===character1) : node.matchesSelector(selector));
}
match && (e.sourceTarget=node);
}
}
}
if (outsideEvent && !match) {
// there is a match for the outside-event:
// we need to set e.sourceTarget and e.target:
newTarget = DOCUMENT.getElement(selector, true);
if (newTarget) {
e.sourceTarget = node;
subscriber.t = newTarget;
}
else {
// make return `false` because the selector is not in the dom
match = true;
}
}
console.log(NAME, '_domSelToFunc filter returns '+(!outsideEvent ? match : !match));
return !outsideEvent ? match : !match;
};
return true;
};
// at this point, we need to find out what are the current node-refs. whenever there is
// a filter that starts with `#` --> in those cases we have a bubble-chain, because the selector isn't
// set up with `document` at its root.
// we couldn't do this at time of subscribtion, for the nodes might not be there at that time.
// however, we only need to do this once: we store the value if we find them
// no problem when the nodes leave the dom later: the previous filter wouldn't pass
_findCurrentTargets = function(subscribers) {
console.log(NAME, '_findCurrentTargets');
subscribers.forEach(
function(subscriber) {
console.log(NAME, '_findCurrentTargets for single subscriber. nId: '+subscriber.nId);
subscriber.nId && (subscriber.n=DOCUMENT.getElementById(subscriber.nId, true));
}
);
};
/*
* Generates an event through our Event-system. Does the actual transportation from DOM-events
* into our Eventsystem. It also looks at the response of our Eventsystem: if our system
* halts or preventDefaults the customEvent, then the original DOM-event will be preventDefaulted.
*
* @method _evCallback
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_evCallback = function(e) {
console.log(NAME, '_evCallback');
var allSubscribers = Event._subs,
eType = e.type,
eventobject, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs, subsOutside,
subscribers, eventobjectOutside, wildcard_named_subsOutside, customEvent, eventName, which;
eventName = eType;
// first: a `click` event might be needed to transformed into `rightclick`:
if (eventName===CLICK) {
which = e.which;
(which===2) && (eventName=CENTERCLICK);
(which===3) && (eventName=RIGHTCLICK);
}
if (eventName==='tap') {
// prevent the next click-event
preventClick = true;
e.clientX || (e.clientX = e.center && e.center.x);
e.clientY || (e.clientY = e.center && e.center.y);
}
else if (preventClick && (eventName===CLICK)) {
preventClick = false;
return;
}
if (eventName===CLICK) {
eventName = 'tap';
e.center = {
x: e.clientX,
y: e.clientY
};
e.eventType = 4;
e.pointerType = 'mouse';
e.tapCount = 1;
}
customEvent = 'UI:'+eventName;
subs = allSubscribers[customEvent];
wildcard_named_subs = allSubscribers['*:'+eventName];
named_wildcard_subs = allSubscribers['UI:*'];
wildcard_wildcard_subs = allSubscribers['*:*'];
// Emit the dom-event though our eventsystem:
// NOTE: emit() needs to be synchronous! otherwise we wouldn't be able
// to preventDefault in time
//
// e = eventobject from the DOM-event OR gesture-event
// eventobject = eventobject from our Eventsystem, which get returned by calling `emit()`
// now so the work:
subscribers = _getSubscribers(e, true, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
eventobject = Event._emit(e.target, customEvent, e, subscribers, [], _preProcessor, false, true);
// now check outside subscribers
subsOutside = allSubscribers[customEvent+OUTSIDE];
wildcard_named_subsOutside = allSubscribers['*:'+eventName+OUTSIDE];
subscribers = _getSubscribers(e, true, subsOutside, wildcard_named_subsOutside);
eventobjectOutside = Event._emit(e.target, customEvent+OUTSIDE, e, subscribers, [], _preProcessor, false, true);
// if eventobject was preventdefaulted or halted: take appropriate action on
// the original dom-event. Note: only the original event can caused this, not the outsideevent
// stopPropagation on the original eventobject has no impact on our eventsystem, but who know who else is watching...
// be carefull though: not all gesture events have e.stopPropagation
eventobject.status.halted && e.stopPropagation && e.stopPropagation();
// now we might need to preventDefault the original event.
// be carefull though: not all gesture events have e.preventDefault
if ((eventobject.status.halted || eventobject.status.defaultPrevented || eventobject.status.defaultPreventedContinue) && e.preventDefault) {
e.preventDefault();
}
if (eventobject.status.ok) {
// last step: invoke the aftersubscribers
// we need to do this asynchronous: this way we pass them AFTER the DOM-event's defaultFn
// also make sure to paas-in the payload of the manipulated eventobject
subscribers = _getSubscribers(e, false, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent, eventobject, [], subscribers, _preProcessor, true), 10);
// now check outside subscribers
subscribers = _getSubscribers(e, false, subsOutside, wildcard_named_subsOutside);
(subscribers.length>0) && later(Event._emit.bind(Event, e.target, customEvent+OUTSIDE, eventobjectOutside, [], subscribers, _preProcessor, true), 10);
}
};
/*
* Creates an array of subscribers in the right order, conform their position in the DOM.
* Only subscribers that match the filter are involved.
*
* @method _getSubscribers
* @param e {Object} eventobject
* @param before {Boolean} whether it is a before- or after-subscriber
* @param subs {Array} array with subscribers
* @param wildcard_named_subs {Array} array with subscribers
* @param named_wildcard_subs {Array} array with subscribers
* @param wildcard_wildcard_subs {Array} array with subscribers
* @private
* @since 0.0.1
*/
_getSubscribers = function(e, before, subs, wildcard_named_subs, named_wildcard_subs, wildcard_wildcard_subs) {
var subscribers = [],
beforeOrAfter = before ? 'b' : 'a',
saveConcat = function(extrasubs) {
extrasubs && extrasubs[beforeOrAfter] && (subscribers=subscribers.concat(extrasubs[beforeOrAfter]));
};
saveConcat(subs);
saveConcat(wildcard_named_subs);
saveConcat(named_wildcard_subs);
saveConcat(wildcard_wildcard_subs);
if (subscribers.length>0) {
subscribers = function(array, testFunc) {
// quickest way to filter an array: see http://jsperf.com/array-filter-performance/4
var filtered = array.slice(0), i;
for (i=array.length-1; i>=0; i--) {
console.log(NAME, 'filtercheck for subscriber');
testFunc(array[i]) || filtered.splice(i, 1);
}
return filtered;
}(subscribers, function(subscriber) {return (!subscriber.f || subscriber.f.call(subscriber.o, e));});
if (subscribers.length>0) {
// _findCurrentTargets(subscribers);
// sorting, based upon the sortFn
subscribers.sort(SORT);
}
}
return subscribers;
};
/*
* Sets e.target and e.sourceTarget for the single subscriber.
* Needs to be done for evenry single subscriber, because with a single event, these values change for each subscriber
*
* @method _preProcessor
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_preProcessor = function(subscriber, e) {
console.log(NAME, '_preProcessor');
// inside the aftersubscribers, we may need exit right away.
// this would be the case whenever stopPropagation or stopImmediatePropagation was called
// in case the subscribernode equals the node on which stopImmediatePropagation was called: return true
var propagationStopped, immediatePropagationStopped,
targetnode = (subscriber.t || subscriber.n);
immediatePropagationStopped = e.status.immediatePropagationStopped;
if (immediatePropagationStopped && ((immediatePropagationStopped===targetnode) || !immediatePropagationStopped.contains(targetnode))) {
console.log(NAME, '_preProcessor will return true because of immediatePropagationStopped');
return true;
}
// in case the subscribernode does not fall within or equals the node on which stopPropagation was called: return true
propagationStopped = e.status.propagationStopped;
if (propagationStopped && (propagationStopped!==targetnode) && !propagationStopped.contains(targetnode)) {
console.log(NAME, '_preProcessor will return true because of propagationStopped');
return true;
}
e._s = subscriber._s;
// now we might need to set e.target to the right node:
// the filterfunction might have found the true domnode that should act as e.target
// and set it at subscriber.t
// also, we need to backup the original e.target: this one should be reset when
// we encounter a subscriber with its own filterfunction instead of selector
if (subscriber.t) {
e.sourceTarget || (e.sourceTarget=e.target);
e.target = subscriber.t;
}
else {
e.sourceTarget && (e.target=e.sourceTarget);
}
return false;
};
/*
* Transports DOM-events to the Event-system. Catches events at their most early stage:
* their capture-phase. When these events happen, a new customEvent is generated by our own
* Eventsystem, by calling _evCallback(). This way we keep DOM-events and our Eventsystem completely separated.
*
* @method _setupDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @param subscriber {Object} subscriber
* @param subscriber.o {Object} context
* @param subscriber.cb {Function} callbackFn
* @param subscriber.f {Function|String} filter
* @private
* @since 0.0.1
*/
_setupDomListener = function(customEvent, subscriber) {
console.log(NAME, '_setupDomListener');
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0],
eventName = eventSplitted[1],
outsideEvent = REGEXP_UI_OUTSIDE.test(eventName);
// be careful: anyone could also register an `outside`-event.
// in those cases, the DOM-listener must be set up without `outside`
outsideEvent && (eventName=eventName.substring(0, eventName.length-7));
// if eventName equals `mouseover` or `mouseleave` then we quit:
// people should use `mouseover` and `mouseout`
if ((eventName==='mouseenter') || (eventName==='mouseleave')) {
console.warn(NAME, 'Subscription to '+eventName+' not supported, use mouseover and mouseout: this eventsystem uses these non-noisy so they act as mouseenter and mouseleave');
return;
}
// only accept tap-events, yet later on we WILL need to listen for click-events
(eventName===CLICK) && (eventName=TAP);
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(emitterName+':'+eventName+(outsideEvent ? OUTSIDE : ''), subscriber);
// already registered? then return, also return if someone registered for UI:*
if (DOMEvents[eventName] || (eventName==='*')) {
// cautious: one might have registered the event, but not yet the outsideevent.
// in that case: save this setting:
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
return;
}
DOMEvents[eventName] = true;
if (outsideEvent) {
DOMEvents[eventName+OUTSIDE] = true;
(eventName===TAP) && (DOMEvents[CLICK+OUTSIDE]=true);
}
// one exception: windowresize should listen to the window-object
if (eventName==='resize') {
window.addEventListener(eventName, _evCallback);
}
else {
((eventName===RIGHTCLICK) || (eventName===CENTERCLICK)) && (eventName=TAP);
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.addEventListener(eventName, _evCallback, true);
// listen for both `tap` and `click` events to happen
(eventName===TAP) && DOCUMENT.addEventListener(CLICK, _evCallback, true);
}
};
_setupEvents = function() {
var lastFocussed;
// make sure disabled buttons don't work:
Event.before(['tap', 'press'], function(e) {
e.preventDefault();
}, '.pure-button-disabled, button[disabled]');
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.before(
'keydown',
function(e) {
e._buttonPressed = true;
Event.emit(e.target, 'UI:tap', e);
},
function(e) {
var keyCode = e.keyCode;
return (e.target.getTagName()==='BUTTON') && ((keyCode===13) || (keyCode===32));
}
);
// make sure that a focussed button which recieves an keypress also fires the `tap`-event
// note: the `click`-event will always be fired by the browser
Event.after(
'tap',
function(e) {
var buttonNode = e.target;
if (e._buttonPressed) {
buttonNode.setClass(PURE_BUTTON_ACTIVE);
e._noRender = true;
// even if the node isn't in the DOM, we can still try to manipulate it:
// the vdom makes sure no errors occur when the node is already removed
later(buttonNode.removeClass.bind(buttonNode, PURE_BUTTON_ACTIVE), TIME_BTN_PRESSED);
}
}
);
// fix activeElement on Mac
Event.before('focus', function(e) {
// will come here more often because of bubblechain
// however, the last pass-through will set the deepest node
lastFocussed = e.target;
});
// patching DOCUMENT.activeElement because it doesn't work well in a Mac: https://developer.mozilla.org/en-US/docs/Web/API/document.activeElement
// DOCUMENT._activeElement is used with the patch for DOCUMENT.activeElement its getter
Event.after('focus', function() {
DOCUMENT._activeElement = lastFocussed;
});
Event.before('blur', function() {
DOCUMENT._activeElement = null;
});
// Note: window.document has no prototype
Object.defineProperty(DOCUMENT, 'activeElement', {
get: function() {
return DOCUMENT._activeElement || DOCUMENT.body;
}
});
};
_setupMutationListener = function() {
DOCUMENT.hasMutationSubs = true;
if (!mutationEventsDefined) {
Event.defineEvent(EV_REMOVED).unPreventable();
Event.defineEvent(EV_INSERTED).unPreventable();
Event.defineEvent(EV_CONTENT_CHANGE).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_REMOVED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_CHANGED).unPreventable();
Event.defineEvent(EV_ATTRIBUTE_INSERTED).unPreventable();
mutationEventsDefined = true;
}
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFunc = function(subscriberOne, subscriberTwo) {
return (subscriberTwo.t || subscriberTwo.n).contains(subscriberOne.t || subscriberOne.n) ? -1 : 1;
};
/*
*
* @method _sortFunc
* @param customEvent {String}
* @private
* @return {Function|undefined} sortable function
* @since 0.0.1
*/
_sortFuncReversed = function(subscriberOne, subscriberTwo) {
return (subscriberOne.t || subscriberOne.n).contains(subscriberTwo.t || subscriberTwo.n) ? 1 : -1;
};
/*
* Removes DOM-eventsubscribers from document when they are no longer needed.
*
* @method _teardownDomListener
* @param customEvent {String} the customEvent that is transported to the eventsystem
* @private
* @since 0.0.2
*/
_teardownDomListener = function(customEvent) {
var customEventWithoutOutside = customEvent.endsWith(OUTSIDE) ? customEvent.substr(0, customEvent.length-7) : customEvent,
eventSplitted = customEventWithoutOutside.split(':'),
eventName = eventSplitted[1],
stillInUse;
if ((customEventWithoutOutside===CLICK) || (customEventWithoutOutside===RIGHTCLICK) || (customEventWithoutOutside===CENTERCLICK)) {
stillInUse = Event._subs[CLICK] ||
Event._subs[CLICK+OUTSIDE];
Event._subs[RIGHTCLICK] ||
Event._subs[RIGHTCLICK+OUTSIDE],
Event._subs[CENTERCLICK] ||
Event._subs[CENTERCLICK+OUTSIDE];
eventName = CLICK;
}
else {
stillInUse = Event._subs[customEventWithoutOutside] || Event._subs[customEventWithoutOutside+OUTSIDE];
}
if (!stillInUse) {
console.log(NAME, '_teardownDomListener '+customEvent);
// remove eventlistener from `document`
// one exeption: windowresize should listen to the window-object
if (eventName==='resize') {
window.removeEventListener(eventName, _evCallback);
}
else {
// important: set the third argument `true` so we listen to the capture-phase.
DOCUMENT.removeEventListener(eventName, _evCallback, true);
}
delete DOMEvents[eventName];
}
};
_teardownMutationListener = function() {
if (!Event._subs[EV_REMOVED] &&
!Event._subs[EV_INSERTED] &&
!Event._subs[EV_CONTENT_CHANGE] &&
!Event._subs[EV_ATTRIBUTE_REMOVED] &&
!Event._subs[EV_ATTRIBUTE_CHANGED] &&
!Event._subs[EV_ATTRIBUTE_INSERTED]
) {
DOCUMENT.hasMutationSubs = false;
}
};
// Now a very tricky one:
// Some browsers do an array.sort down-top instead of top-down.
// In those cases we need another sortFn, for the position on an equal match should fall
// behind instead of before (which is the case on top-down sort)
[1,2].sort(function(a /*, b */) {
SORT || (SORT=(a===2) ? _sortFuncReversed : _sortFunc);
});
// Now we do some initialization in order to make DOM-events work:
Object.defineProperty(Event._defaultEventObj, 'currentTarget', {
get: function() {
var ev = this,
e_target = ev.target;
if (e_target && ev._s) {
if (e_target.matches(ev._s)) {
return e_target;
}
return e_target.inside(ev._s) || undefined;
}
}
});
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(UI+'*', _setupDomListener, Event)
._setEventObjProperty('stopPropagation', function() {this.status.ok || (this.status.propagationStopped = this.target);})
._setEventObjProperty('stopImmediatePropagation', function() {this.status.ok || (this.status.immediatePropagationStopped = this.target);});
// Notify when someone subscribes to any event at all --> we might need to transform the filterFn from a selector into a true fnction
// this is already done automaticly by _setupDomListener fo UI:* events
Event.notify('*:*', function(customEvent, subscriber) {
var eventSplitted = customEvent.split(':'),
emitterName = eventSplitted[0];
if ((emitterName!=='UI') && (typeof subscriber.f==='string')) {
// now transform the subscriber's filter from css-string into a filterfunction
_selToFunc(customEvent, subscriber);
}
}, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(UI+'*', _teardownDomListener, Event);
Event._sellist = [_domSelToFunc];
_setupEvents();
// making HTMLElement to be able to emit using event-emitter:
(function(HTMLElementPrototype) {
HTMLElementPrototype.merge(Event.Emitter('UI'));
}(window.HTMLElement.prototype));
// Notify when someone subscribes to an UI:* event
// if so: then we might need to define a customEvent for it:
// alse define the specific DOM-methods that can be called on the eventobject: `stopPropagation` and `stopImmediatePropagation`
Event.notify(MUTATION_EVENTS, _setupMutationListener, Event);
// Notify when someone detaches an UI:* event
// if so: then we might need to detach the native listener on `document`
Event.notifyDetach(MUTATION_EVENTS, _teardownMutationListener, Event);
// Note: window.document has no prototype
DOCUMENT.suppressMutationEvents = function(suppress) {
this._suppressMutationEvents = suppress;
};
// Event.noDeepDomEvt and Event._domCallback are the only method that is added to Event.
// We need to do this, because `event-mobile` needs access to the same method.
// We could have done without this method and instead listen for a custom-event to handle
// Mobile events, however, that would lead into 2 eventcycli which isn't performant.
/**
*
* @method noDeepDomEvt
* @param domEvent {String} the eventName that should be processed without deepsearch
* @param e {Object} eventobject
* @for Event
* @chainable
* @since 0.0.1
*/
Event.noDeepDomEvt = function(domEvent) {
domEvent.contains(':') || (domEvent=UI+domEvent);
NO_DEEP_SEARCH[domEvent] = true;
return this;
};
/**
* Does the actual transportation from DOM-events into the Eventsystem. It also looks at the response of
* the Eventsystem: on e.halt() or e.preventDefault(), the original DOM-event will be preventDefaulted.
*
* @method _domCallback
* @param eventName {String} the customEvent that is transported to the eventsystem
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
Event._domCallback = function(e) {
_evCallback(e);
};
// store module:
window._ITSAmodules.EventDom = Event;
return Event;
};
},{"event":3859,"js-ext/extra/hashmap.js":3875,"js-ext/lib/array.js":3876,"js-ext/lib/object.js":3877,"js-ext/lib/string.js":3878,"polyfill/polyfill-base.js":3890,"utils":3891,"vdom":3949}],3856:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":3861,"js-ext/lib/object.js":3862,"polyfill/polyfill-base.js":3874}],3857:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":3856}],3858:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":3856,"js-ext/extra/classes.js":3860,"js-ext/lib/object.js":3862}],3859:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":3856,"./event-emitter.js":3857,"./event-listener.js":3858}],3860:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":3862,"js-ext/extra/hashmap.js":3861,"polyfill/polyfill-base.js":3865}],3861:[function(require,module,exports){
module.exports=require(4)
},{}],3862:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3861,"polyfill/polyfill-base.js":3865,"utils":3866}],3863:[function(require,module,exports){
module.exports=require(14)
},{}],3864:[function(require,module,exports){
module.exports=require(15)
},{}],3865:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3863,"./lib/window.console.js":3864}],3866:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3867,"./lib/timers.js":3868}],3867:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3861,"polyfill/polyfill-base.js":3871}],3868:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3871}],3869:[function(require,module,exports){
module.exports=require(14)
},{}],3870:[function(require,module,exports){
module.exports=require(15)
},{}],3871:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3869,"./lib/window.console.js":3870}],3872:[function(require,module,exports){
module.exports=require(14)
},{}],3873:[function(require,module,exports){
module.exports=require(15)
},{}],3874:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3872,"./lib/window.console.js":3873}],3875:[function(require,module,exports){
module.exports=require(4)
},{}],3876:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":3881}],3877:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3875,"polyfill/polyfill-base.js":3881,"utils":3882}],3878:[function(require,module,exports){
module.exports=require(29)
},{}],3879:[function(require,module,exports){
module.exports=require(14)
},{}],3880:[function(require,module,exports){
module.exports=require(15)
},{}],3881:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3879,"./lib/window.console.js":3880}],3882:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3883,"./lib/timers.js":3884}],3883:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3875,"polyfill/polyfill-base.js":3887}],3884:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3887}],3885:[function(require,module,exports){
module.exports=require(14)
},{}],3886:[function(require,module,exports){
module.exports=require(15)
},{}],3887:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3885,"./lib/window.console.js":3886}],3888:[function(require,module,exports){
module.exports=require(14)
},{}],3889:[function(require,module,exports){
module.exports=require(15)
},{}],3890:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3888,"./lib/window.console.js":3889}],3891:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3892,"./lib/timers.js":3893}],3892:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3875,"polyfill/polyfill-base.js":3896}],3893:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3896}],3894:[function(require,module,exports){
module.exports=require(14)
},{}],3895:[function(require,module,exports){
module.exports=require(15)
},{}],3896:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3894,"./lib/window.console.js":3895}],3897:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],3898:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":3902,"js-ext/extra/hashmap.js":3899,"polyfill/polyfill-base.js":3908}],3899:[function(require,module,exports){
module.exports=require(4)
},{}],3900:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":3901,"../lib/object.js":3902,"./classes.js":3898,"js-ext/extra/hashmap.js":3899,"polyfill/lib/weakmap.js":3906}],3901:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":3908}],3902:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3899,"polyfill/polyfill-base.js":3908,"utils":3909}],3903:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":3908}],3904:[function(require,module,exports){
module.exports=require(29)
},{}],3905:[function(require,module,exports){
module.exports=require(14)
},{}],3906:[function(require,module,exports){
module.exports=require(57)
},{}],3907:[function(require,module,exports){
module.exports=require(15)
},{}],3908:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3905,"./lib/window.console.js":3907}],3909:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3910,"./lib/timers.js":3911}],3910:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3899,"polyfill/polyfill-base.js":3914}],3911:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3914}],3912:[function(require,module,exports){
module.exports=require(14)
},{}],3913:[function(require,module,exports){
module.exports=require(15)
},{}],3914:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3912,"./lib/window.console.js":3913}],3915:[function(require,module,exports){
module.exports=require(66)
},{}],3916:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":3915}],3917:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":3915}],3918:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":3915}],3919:[function(require,module,exports){
module.exports=require(14)
},{}],3920:[function(require,module,exports){
module.exports=require(15)
},{}],3921:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3919,"./lib/window.console.js":3920}],3922:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3923,"./lib/timers.js":3924}],3923:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3899,"polyfill/polyfill-base.js":3927}],3924:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3927}],3925:[function(require,module,exports){
module.exports=require(14)
},{}],3926:[function(require,module,exports){
module.exports=require(15)
},{}],3927:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3925,"./lib/window.console.js":3926}],3928:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":3929}],3929:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":3930,"js-ext/lib/object.js":3931}],3930:[function(require,module,exports){
module.exports=require(4)
},{}],3931:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3930,"polyfill/polyfill-base.js":3934,"utils":3935}],3932:[function(require,module,exports){
module.exports=require(14)
},{}],3933:[function(require,module,exports){
module.exports=require(15)
},{}],3934:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3932,"./lib/window.console.js":3933}],3935:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3936,"./lib/timers.js":3937}],3936:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3930,"polyfill/polyfill-base.js":3940}],3937:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3940}],3938:[function(require,module,exports){
module.exports=require(14)
},{}],3939:[function(require,module,exports){
module.exports=require(15)
},{}],3940:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3938,"./lib/window.console.js":3939}],3941:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":3899,"js-ext/lib/object.js":3902,"js-ext/lib/string.js":3904,"polyfill":3921,"polyfill/extra/transition.js":3916,"polyfill/extra/vendorCSS.js":3918}],3942:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":3899,"js-ext/lib/object.js":3902,"polyfill":3921}],3943:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":3899,"js-ext/lib/object.js":3902,"js-ext/lib/string.js":3904,"polyfill":3921}],3944:[function(require,module,exports){
arguments[4][95][0].apply(exports,arguments)
},{"../css/element.css":3897,"./attribute-extractor.js":3941,"./element-array.js":3942,"./html-parser.js":3945,"./node-parser.js":3946,"./vdom-ns.js":3947,"./vnode.js":3948,"js-ext/extra/hashmap.js":3899,"js-ext/lib/object.js":3902,"js-ext/lib/promise.js":3903,"js-ext/lib/string.js":3904,"polyfill":3921,"polyfill/extra/transition.js":3916,"polyfill/extra/transitionend.js":3917,"polyfill/extra/vendorCSS.js":3918,"utils":3922,"window-ext":3928}],3945:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":3941,"./vdom-ns.js":3947,"js-ext/extra/hashmap.js":3899,"js-ext/lib/object.js":3902,"polyfill":3921}],3946:[function(require,module,exports){
arguments[4][97][0].apply(exports,arguments)
},{"./attribute-extractor.js":3941,"./vdom-ns.js":3947,"./vnode.js":3948,"js-ext/extra/hashmap.js":3899,"js-ext/lib/object.js":3902,"polyfill":3921}],3947:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":3899,"js-ext/lib/object.js":3902,"polyfill":3921}],3948:[function(require,module,exports){
"use strict";
/**
* Delivers the `vnode` prototype object, which is a virtualisation of an `Element` inside the Dom.
* These Elements work smoothless with the vdom (see ...).
*
* vnodes are much quicker to access and walk through than native dom-nodes. However, this is a module you don't need
* by itself: `Element`-types use these features under the hood.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* <br>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
*
* @module vdom
* @submodule vnode
* @class vnode
* @since 0.0.1
*/
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('js-ext/lib/string.js');
require('polyfill');
var createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.VNode) {
return window._ITSAmodules.VNode; // VNODE was already created
}
console.warn('VDOM 9');
var NS = require('./vdom-ns.js')(window),
extractor = require('./attribute-extractor.js')(window),
DOCUMENT = window.document,
LightMap = require('js-ext/extra/lightmap.js'),
MUTATION_EVENTS = new LightMap(),
BATCH_WILL_RUN = false,
xmlNS = NS.xmlNS,
nodeids = NS.nodeids,
htmlToVNodes = require('./html-parser.js')(window),
timers = require('utils/lib/timers.js'),
async = timers.async,
later = timers.later,
/*jshint proto:true */
PROTO_SUPPORTED = !!Object.__proto__,
/*jshint proto:false */
// cleanup memory after 1 minute: removed nodes SHOULD NOT be accessed afterwards
// because vnode would be recalculated and might be different from before
DESTROY_DELAY = 60000,
unescapeEntities = NS.UnescapeEntities,
NTH_CHILD_REGEXP = /^(?:(\d*)[n|N])([\+|\-](\d+))?$/, // an+b
STRING = 'string',
CLASS = 'class',
STYLE = 'style',
ID = 'id',
NODE= 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
EV_REMOVED = NODE+REMOVE,
EV_INSERTED = NODE+INSERT,
EV_CONTENT_CHANGE = NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = ATTRIBUTE+INSERT,
SPLIT_CHARACTER = createHashMap({
' ': true,
'>': true,
'+': true, // only select the element when it is immediately preceded by the former element
'~': true // only the element when it has the former element as a sibling. (just like `+`, but less strict)
}),
STORABLE_SPLIT_CHARACTER = createHashMap({
'>': true,
'+': true,
'~': true
}),
SIBLING_MATCH_CHARACTER = createHashMap({
'+': true,
'~': true
}),
ATTR_DETAIL_SPECIFIERS = createHashMap({
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to attribute-name end-tokens.
*
* @property END_ATTRIBUTENAME
* @default {
* '=': true,
* ']': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
END_ATTRIBUTENAME = createHashMap({
'=': true,
']': true,
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to different changes of Element nodeType changes.
*
* @property NODESWITCH
* @default {
* 1: {
* 1: 1,
* 3: 2,
* 8: 3
* },
* 3: {
* 1: 4,
* 3: 5,
* 8: 6
* },
* 8: {
* 1: 7,
* 3: 8,
* 8: 9
* }
* }
* @type Object
* @protected
* @since 0.0.1
*/
NODESWITCH = createHashMap({
1: createHashMap({
1: 1, // oldNodeType==Element, newNodeType==Element
3: 2, // oldNodeType==Element, newNodeType==TextNode
8: 3 // oldNodeType==Element, newNodeType==Comment
}),
3: createHashMap({
1: 4, // oldNodeType==TextNode, newNodeType==Element
3: 5, // oldNodeType==TextNode, newNodeType==TextNode
8: 6 // oldNodeType==TextNode, newNodeType==Comment
}),
8: createHashMap({
1: 7, // oldNodeType==Comment, newNodeType==Element
3: 8, // oldNodeType==Comment, newNodeType==TextNode
8: 9 // oldNodeType==Comment, newNodeType==Comment
})
}),
/**
* Object to gain quick access to selector start-tokens.
*
* @property SELECTOR_IDENTIFIERS
* @default {
* '#': 1,
* '.': 2,
* '[': 3
* }
* @type Object
* @protected
* @since 0.0.1
*/
SELECTOR_IDENTIFIERS = createHashMap({
'#': 1,
'.': 2,
'[': 3,
':': 4
}),
PSEUDO_FIRST_CHILD = ':first-child',
PSEUDO_FIRST_OF_TYPE = ':first-of-type',
PSEUDO_LAST_CHILD = ':last-child',
PSEUDO_LAST_OF_TYPE = ':last-of-type',
PSEUDO_NTH_CHILD = ':nth-child',
PSEUDO_NTH_LAST_CHILD = ':nth-last-child',
PSEUDO_NTH_LAST_OF_TYPE = ':nth-last-of-type',
PSEUDO_NTH_OF_TYPE = ':nth-of-type',
PSEUDO_ONLY_OF_TYPE = ':only-of-type',
PSEUDO_ONLY_CHILD = ':only-child',
/**
* Object to gain quick access to the selectors that required children
*
* @property PSEUDO_REQUIRED_CHILDREN
* @default {
* ':first-child': true,
* ':first-of-type': true,
* ':last-child': true,
* ':last-of-type': true,
* ':nth-child': true,
* ':nth-last-child': true,
* ':nth-last-of-type': true,
* ':nth-of-type': true,
* ':only-of-type': true,
* ':only-child': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
PSEUDO_REQUIRED_CHILDREN = createHashMap(),
_matchesSelectorItem, _matchesOneSelector, _findElementSibling, vNodeProto, _markRemoved, _tryReplaceChild,
_splitSelector, _findNodeSibling, _matchNthChild, _batchEmit, _emitDestroyChildren, _tryRemoveDomNode;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_CHILD] = true;
/**
* Searches for the next -or previous- node-sibling (nodeType of 1, 3 or 8).
*
* @method _findNodeSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findNodeSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
index = vParent.vChildNodes.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildNodes[index];
};
/**
* Searches for the next -or previous- Element-sibling (nodeType of 1).
*
* @method _findElementSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findElementSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
if (vnode.nodeType===1) {
index = vParent.vChildren.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildren[index];
}
else {
/*jshint noempty:true */
while ((vnode=_findNodeSibling(vnode, next)) && (vnode.nodeType!==1)) {}
/*jshint noempty:false */
return vnode;
}
};
/**
* Check whether the vnode matches a "nth-child" test, which is used for css pseudoselectors like `nth-child`, `nth-of-type` etc.
*
* @method _matchNthChild
* @param pseudoArg {String} the argument for nth-child
* @param index {Number} the index of the inspected vnode
* @return {Boolean} whether the vnode matches the nthChild test
* @protected
* @private
* @since 0.0.1
*/
_matchNthChild = function(pseudoArg, index) {
var match, k, a, b, nodeOk, nthIndex, sign, isNumber;
(pseudoArg==='even') && (pseudoArg='2n');
(pseudoArg==='odd') && (pseudoArg='2n+1');
match = pseudoArg.match(NTH_CHILD_REGEXP) || (isNumber=pseudoArg.validateNumber());
if (!match) {
return false;
}
// pseudoArg follows the pattern: `an+b`
if (!isNumber) {
a = match[1];
sign = match[2];
b = match[3] || 0;
(b==='') && (b=0);
}
else {
b = pseudoArg;
}
sign && (sign=sign[0]);
if (!a) {
// only fixed index to match
return (sign==='-') ? false : (parseInt(b, 10)===index);
}
else {
// we need to iterate
nodeOk = false;
b = window.Number(b);
for (k=0; !nodeOk; k++) {
nthIndex = (sign==='-') ? (a*k) - b : (a*k) + b;
if (nthIndex===index) {
nodeOk = true;
}
else if (nthIndex>index) {
// beyond index --> will never become a fix anymore
return false;
}
}
return nodeOk;
}
};
/**
* Check whether the vnode matches the css-selector. the css-selector should be a single selector,
* not multiple, so it shouldn't contain a `comma`.
*
* @method _matchesOneSelector
* @param vnode {vnode} the vnode to inspect
* @param selector {String} the selector-item to check the match for
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches the css-selector
* @protected
* @private
* @since 0.0.1
*/
_matchesOneSelector = function(vnode, selector, relatedVNode) {
var selList = _splitSelector(selector),
size = selList.length,
selMatch = false,
i, selectorItem, last,
rightvnode, relationMatch, checkRelation;
if (STORABLE_SPLIT_CHARACTER[selList[size-1]]) {
return false;
}
relationMatch = function(leftVNode, rightVNode, relationCharacter) {
var match, vParent, vChildren;
// when `selector` starts with `>`, `~` or `+`, then
// there should also be a match comparing a related node!
switch (relationCharacter) {
case '>':
leftVNode || (leftVNode=rightVNode.vParent);
match = (leftVNode.vChildren.indexOf(rightVNode)!==-1);
break;
case '~':
leftVNode || (leftVNode=rightVNode.vFirstElement);
vParent = leftVNode.vParent;
vChildren = vParent && vParent.vChildren;
match = vParent && (vChildren.indexOf(leftVNode)<vChildren.indexOf(rightVNode));
break;
case '+':
leftVNode || (leftVNode=rightVNode.vPreviousElement);
match = (leftVNode.vNextElement === rightVNode);
}
return !!match;
};
last = size - 1;
i = last;
while (vnode && ((selMatch || (i===last)) && (i>=0))) {
selectorItem = selList[i];
if (STORABLE_SPLIT_CHARACTER[selectorItem]) {
checkRelation = selectorItem;
i--;
}
else {
selMatch = _matchesSelectorItem(vnode, selectorItem);
if (!selMatch && (i===last)) {
return false;
}
while (!selMatch && vnode && (i!==last)) {
// may also look at nodes higher in the chain
vnode = SIBLING_MATCH_CHARACTER[selList[i+1]] ? vnode.vPreviousElement : vnode.vParent;
selMatch = vnode && _matchesSelectorItem(vnode, selectorItem);
}
selMatch && checkRelation && vnode && (selMatch=relationMatch(vnode, rightvnode, checkRelation));
rightvnode = vnode;
if (vnode) {
// do a precheck on the next checkRelation:
// and determine whether to set the vnode upper the tree or at the previous sibling:
vnode = SIBLING_MATCH_CHARACTER[selList[i-1]] ? vnode.vPreviousElement : vnode.vParent;
}
if (selMatch) {
checkRelation = false;
i--;
}
}
}
if ((rightvnode===relatedVNode) || (i!==-1)) {
selMatch = false;
}
selMatch && checkRelation && rightvnode && (selMatch=relationMatch(relatedVNode, rightvnode, checkRelation));
return selMatch;
};
/**
* Check whether the vnode matches one specific selector-item. Suppose the css-selector: "#mynode li.red .blue"
* then there are 3 selector-items: "#mynode", "li.red" and ".blue"
*
* This method also can handle the new selectors:
* <ul>
* <li>[att^=val] –-> the “begins with” selector</li>
* <li>[att$=val] –-> the “ends with” selector</li>
* <li>[att*=val] –-> the “contains” selector (might be a substring)</li>
* <li>[att~=val] –-> the “contains” selector as a separate word, separated by spaces</li>
* <li>[att|=val] –-> the “contains” selector as a separate word, separated by `|`</li>
* <li>+ --> (same level)</li>
* <li>~ --> (same level)</li>
* </ul>
*
* @method _matchesSelectorItem
* @param vnode {Object} the vnode to inspect
* @param selectorItem {String} the selector-item to check the match for
* @return {Boolean} whether the vnode matches the selector-item
* @protected
* @private
* @since 0.0.1
*/
_matchesSelectorItem = function (vnode, selectorItem) {
var i = 0,
len = selectorItem.length,
character = selectorItem[0],
tagName, id, className, attributeName, attributeValue, stringMarker, attributeisString, isBoolean, insideAttributeValue, insideAttribute,
vParent, checkBoolean, treatment, k, min, max, value, len2, index, found, pseudo, pseudoArg, arglevel, count, vParentVChildren;
if (selectorItem==='*') {
return true;
}
if (!SELECTOR_IDENTIFIERS[character]) {
// starts with tagName
tagName = '';
// reposition i to continue in the right way:
i--;
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
tagName += character;
}
if (tagName.toUpperCase()!==vnode.tag) {
return false;
}
}
while (i<len) {
switch (character) {
case '#':
id = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
id += character;
}
if (id!==vnode.id) {
return false;
}
break;
case '.':
className = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
className += character;
}
if (!vnode.hasClass(className)) {
return false;
}
break;
case '[':
attributeName = '';
while ((++i<len) && (character=selectorItem[i]) && !END_ATTRIBUTENAME[character]) {
attributeName += character;
}
// if character===']' then we have an attribute without a value-definition
if (!vnode.attrs[attributeName] || ((character===']') && (vnode.attrs[attributeName]!==''))) {
return !!vnode.attrs[attributeName];
}
// now we read the value of the attribute
// however, it could be that the selector has a special `detailed` identifier set (defined by: ATTR_DETAIL_SPECIFIERS)
if (ATTR_DETAIL_SPECIFIERS[character]) {
treatment = character; // store the character to know how the attributedata should be treaded
i++; // character should be a "=" by now
}
else {
treatment = null;
}
attributeValue = '';
stringMarker = selectorItem[i+1];
attributeisString = (stringMarker==='"') || (stringMarker==="'");
attributeisString && (i++);
// end of attributaValue = (character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker))
while ((++i<len) && (character=selectorItem[i]) && !((character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker)))) {
attributeValue += character;
}
if (attributeisString) {
// if attribute is string, then we need to _remove to last stringmarker
attributeValue = attributeValue.substr(0, attributeValue.length-1);
}
else {
// if attribute is no string, then we need to typecast its value
isBoolean = ((attributeValue.length>3) && (attributeValue.length<6) &&
(checkBoolean=attributeValue.toUpperCase()) &&
((checkBoolean==='FALSE') || (checkBoolean==='TRUE')));
// typecast the value to either Boolean or Number:
attributeValue = isBoolean ? (checkBoolean==='TRUE') : parseFloat(attributeValue);
}
// depending upon how the attributedata should be treated:
if (treatment) {
switch (treatment) {
case '^': // “begins with” selector
if (!vnode.attrs[attributeName].startsWith(attributeValue)) {
return false;
}
break;
case '$': // “ends with” selector
if (!vnode.attrs[attributeName].endsWith(attributeValue)) {
return false;
}
break;
case '*': // “contains” selector (might be a substring)
if (!vnode.attrs[attributeName].contains(attributeValue)) {
return false;
}
break;
case '~': // “contains” selector as a separate word, separated by spaces
if (!(' '+vnode.attrs[attributeName]+' ').contains(' '+attributeValue+' ')) {
return false;
}
break;
case '|': // “contains” selector as a separate word, separated by `|`
if (!('|'+vnode.attrs[attributeName]+'|').contains('|'+attributeValue+'|')) {
return false;
}
break;
}
}
else if (vnode.attrs[attributeName]!==attributeValue) {
return false;
}
// we still need to increase one position:
(++i<len) && (character=selectorItem[i]);
break;
case ':':
// we have a pseudo-selector
// first, find out which one
// because '::' is a valid start (though without any selection), we start to back the next character as well:
pseudo = ':'+selectorItem[++i];
pseudoArg = '';
vParent = vnode.vParent;
vParentVChildren = vParent && vParent.vChildren;
// pseudo-selectors might have an argument passed in, like `:nth-child(2n+1)` or `:not([type="checkbox"])` --> we
// store this argument inside `pseudoArg`
// also note that combinations are possible with `:not` --> `:not(:nth-child(2n+1))`
// also note that we cannot "just" look for a closing character when running into the usage of attributes:
// for example --> `:not([data-x="some data :)"])`
// that's why -once we are inside attribute-data- we need to continue until the attribute-data ends
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
if (character==='(') {
// starting arguments
arglevel = 1;
insideAttribute = false;
insideAttributeValue = false;
while ((++i<len) && (character=selectorItem[i]) && (arglevel>0)) {
if (!insideAttribute) {
if (character==='(') {
arglevel++;
}
else if (character===')') {
arglevel--;
}
else if (character==='[') {
insideAttribute = true;
}
}
else {
// inside attribute
if (!insideAttributeValue) {
if ((character==='"') || (character==="'")) {
insideAttributeValue = true;
stringMarker = character;
}
else if (character===']') {
insideAttribute = false;
}
}
else if ((character===stringMarker) && (selectorItem[i+1]===']')) {
insideAttributeValue = false;
}
}
(arglevel>0) && (pseudoArg+=character);
}
}
else {
pseudo += character;
}
}
// now, `pseudo` is known as well as its possible pseudoArg
if (!vParentVChildren && PSEUDO_REQUIRED_CHILDREN[pseudo]) {
return false;
}
switch (pseudo) {
case ':checked': // input:checked Selects every checked <input> element
if (!vnode.attrs.checked) {
return false;
}
break;
case ':disabled': // input:disabled Selects every disabled <input> element
if (!vnode.attrs.disabled) {
return false;
}
break;
case ':empty': // p:empty Selects every <p> element that has no children (including text nodes)
if (vnode.vChildNodes && (vnode.vChildNodes.length>0)) {
return false;
}
break;
case ':enabled': // input:enabled Selects every enabled <input> element
if (vnode.attrs.disabled) {
return false;
}
break;
case PSEUDO_FIRST_CHILD: // p:first-child Selects every <p> element that is the first child of its parent
if (vParentVChildren[0]!==vnode) {
return false;
}
break;
case PSEUDO_FIRST_OF_TYPE: // p:first-of-type Selects every <p> element that is the first <p> element of its parent
for (k=vParentVChildren.indexOf(vnode)-1; k>=0; k--) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':focus': // input:focus Selects the input element which has focus
if (vnode.domNode!==DOCUMENT.activeElement) {
return false;
}
break;
case ':in-range': // input:in-range Selects input elements with a value within a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || (value<min) || (value>max)) {
return false;
}
break;
case ':lang': // p:lang(it) Selects every <p> element with a lang attribute equal to "it" (Italian)
if (vnode.attrs.lang!==pseudoArg) {
return false;
}
break;
case PSEUDO_LAST_CHILD: // p:last-child Selects every <p> element that is the last child of its parent
if (vParentVChildren[vParentVChildren.length-1]!==vnode) {
return false;
}
break;
case PSEUDO_LAST_OF_TYPE: // p:last-of-type Selects every <p> element that is the last <p> element of its parent
len2 = vParentVChildren.length;
for (k=vParentVChildren.indexOf(vnode)+1; k<len2; k++) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':not': // :not(p) Selects every element that is not a <p> element
if (vnode.matchesSelector(pseudoArg)) {
return false;
}
break;
case PSEUDO_NTH_CHILD: // p:nth-child(2) Selects every <p> element that is the second child of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
index = vParentVChildren.indexOf(vnode)+1;
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_CHILD: // p:nth-last-child(2) Selects every <p> element that is the second child of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_OF_TYPE: // p:nth-last-of-type(2) Selects every <p> element that is the second <p> element of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
index = 0;
for (k=vParentVChildren.length-1; (k>=0) && !found; k--) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_OF_TYPE: // p:nth-of-type(2) Selects every <p> element that is the second <p> element of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
len2 = vParentVChildren.length;
index = 0;
for (k=0; (k<len2) && !found; k++) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_ONLY_OF_TYPE: // p:only-of-type Selects every <p> element that is the only <p> element of its parent
len2 = vParentVChildren.length;
count = 0;
for (k=0; (k<len2) && (count<=1); k++) {
(vParentVChildren[k].tag===vnode.tag) && count++;
}
if (count!==1) {
return false;
}
break;
case PSEUDO_ONLY_CHILD: // p:only-child Selects every <p> element that is the only child of its parent
if (vParentVChildren.length!==1) {
return false;
}
break;
case ':optional': // input:optional Selects input elements with no "required" attribute
if (vnode.attrs.required) {
return false;
}
break;
case ':out-of-range': // input:out-of-range Selects input elements with a value outside a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || ((value>=min) && (value<=max))) {
return false;
}
break;
case ':read-only': // input:read-only Selects input elements with the "readonly" attribute specified
if (!vnode.attrs.readonly) {
return false;
}
break;
case ':read-write': // input:read-write Selects input elements with the "readonly" attribute NOT specified
if (vnode.attrs.readonly) {
return false;
}
break;
case ':required': // input:required Selects input elements with the "required" attribute specified
if (!vnode.attrs.required) {
return false;
}
break;
case ':root': // Selects the document's root element
if (vnode.domNode!==DOCUMENT.documentElement) {
return false;
}
break;
}
}
}
return true;
};
/**
* Splits the selector into separate subselector-items that should match different elements through the tree.
* Special characters '>' and '+' are added as separate items in the hash.
*
* @method _splitSelector
* @param selector {String} the selector-item to check the match for
* @return {Array} splitted selectors
* @protected
* @private
* @since 0.0.1
*/
_splitSelector = function(selector) {
var list = [],
len = selector.length,
sel = '',
i, character, insideDataAttr;
for (i=0; i<len; i++) {
character = selector[i];
if (character==='[') {
sel += character;
insideDataAttr = true;
}
else if (character===']') {
sel += character;
insideDataAttr = false;
}
else if (insideDataAttr || !SPLIT_CHARACTER[character]) {
sel += character;
}
else {
// unique selectoritem is found, add it to the list
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
// in case the last character was '>', '+' or '~', we need to add it as a separate item
STORABLE_SPLIT_CHARACTER[character] && (list[list.length]=character);
}
}
// add the last item
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
return list;
};
_batchEmit = function() {
// we will exactly define the 'UI:'-event --> Itags will have another emitterName
MUTATION_EVENTS.each(function (mutationEvents, vnode) {
var domNode = vnode.domNode;
if (mutationEvents[EV_REMOVED]) {
domNode.emit('UI:'+EV_REMOVED);
}
else if (mutationEvents[EV_INSERTED]) {
domNode.emit('UI:'+EV_INSERTED);
}
else {
// contentchange and attributechanges can go hand in hand
mutationEvents.each(function(value, evt) {
domNode.emit('UI:'+evt, (evt===EV_CONTENT_CHANGE) ? null : {changed: value});
});
}
});
MUTATION_EVENTS.clear();
BATCH_WILL_RUN = false;
};
_emitDestroyChildren = function(vnode) {
var children = vnode.vChildren,
len = children.length,
i, vChild;
for (i=0; i<len; i++) {
vChild = children[i];
vChild._emit(EV_REMOVED);
}
};
_markRemoved = function(vnode) {
var vChildNodes = vnode.vChildNodes,
len, i, vChildNode;
if (vnode.nodeType===1) {
Object.protectedProp(vnode, 'removedFromDOM', true);
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && _markRemoved(vChildNode);
}
}
}
};
/**
* A safe way to remove a dom-node, even if it is not present.
* Will not throw a JS error when the "to be removed" node isn't in the dom
*
* @method _tryRemoveDomNode
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param childDomNode {DOMNode} the node that needs to be removed
* @since 0.0.1
*/
_tryRemoveDomNode = function(parentDomNode, childDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && childDomNode.isItag && childDomNode.isItag()) {
DOCUMENT._itagList.remove(childDomNode);
}
try {
parentDomNode._removeChild(childDomNode);
}
catch(err) {}
};
/**
* A safe way to replace a dom-node, even if the "to be replaced"-node it is not present.
* Will not throw a JS error when the "to be replaced" node isn't in the dom. in that case, the new node will
* be appended
*
* @method _tryReplaceChild
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param newChildDomNode {DOMNode} the node that needs to be replaced
* @param oldChildDomNode {DOMNode} the node that needs to be inserted
* @since 0.0.1
*/
_tryReplaceChild = function(parentDomNode, newChildDomNode, oldChildDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && oldChildDomNode.isItag && oldChildDomNode.isItag()) {
DOCUMENT._itagList.remove(oldChildDomNode);
}
try {
parentDomNode._replaceChild(newChildDomNode, oldChildDomNode);
}
catch(err) {
// if the childDomNode isn;t there any more - for whatever reason - the we need to append the newChildNode
parentDomNode._appendChild(newChildDomNode);
}
};
vNodeProto = window._ITSAmodules.VNode = {
/**
* Check whether the vnode's domNode is equal, or contains the specified Element.
*
* @method contains
* @return {Boolean} whether the vnode's domNode is equal, or contains the specified Element.
* @since 0.0.1
*/
contains: function(otherVNode, noProtectedSearch) {
var instance = this;
if (otherVNode && (otherVNode.destroyed || (noProtectedSearch && otherVNode._systemNode))) {
return false;
}
while (otherVNode && (otherVNode!==instance)) {
otherVNode = otherVNode.vParent;
if (otherVNode && noProtectedSearch && (otherVNode._systemNode || (otherVNode.isItag && otherVNode.domNode.contentHidden))) {
return false;
}
}
return (otherVNode===instance);
},
/**
* Empties the vnode.
*
* Syncs with the dom.
*
* @method empty
* @param [full=false] {Boolean} whether system-nodes should be removed as well
* @chainable
* @since 0.0.1
*/
empty: function(full) {
delete this._scripts;
return this._setChildNodes([], null, full);
},
/**
* Returns the first child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method firstOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the first child-vnode or null when not present
* @since 0.0.1
*/
firstOfVChildren: function(cssSelector) {
var instance = this,
found, i, len, vChildren, element;
if (!cssSelector) {
return instance.vFirstElementChild;
}
vChildren = instance.vChildren;
len = vChildren.length;
for (i=0; !found && (i<len); i++) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
return found;
},
/**
* Gets the innerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* Only valid for nodetype=1 (HTMLElements)
*
* @method getHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String|undefined} the innerHTML without the elements specified, or `undefined` when not an HTMLElement
* @since 0.0.1
*/
getHTML: function(exclude, includeSystemNodes) {
var instance = this,
html, vChildNodes, len, i, vChildNode;
if (instance.nodeType===1) {
if (exclude) {
Array.isArray(exclude) || (exclude=[exclude]);
}
html = '';
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
switch (vChildNode.nodeType) {
case 1:
(exclude && exclude.contains(vChildNode.domNode)) || (!includeSystemNodes && vChildNode._systemNode) || (html+=vChildNode.getOuterHTML(exclude, includeSystemNodes));
break;
case 3:
html += vChildNode.text.replace(/</g, '<').replace(/>/g, '>');
break;
case 8:
html += '<!--' + vChildNode.text.replace(/</g, '<').replace(/>/g, '>') + '-->';
}
}
}
return html;
},
/**
* Gets the outerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* @method getOuterHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String} the outerHTML
* @since 0.0.1
*/
getOuterHTML: function(exclude, includeSystemNodes) {
var instance = this,
html,
attrs = instance.attrs;
if (instance.nodeType===1) {
if (instance.nodeType!==1) {
return instance.textContent;
}
html = '<' + instance.tag.toLowerCase();
attrs.each(function(value, key) {
html += ' '+key+'="'+value+'"';
});
instance.isVoid && (html += '/');
html += '>';
if (!instance.isVoid) {
html += ((!includeSystemNodes && instance.isItag) ? '' : instance.getHTML(exclude, includeSystemNodes)) + '</' + instance.tag.toLowerCase() + '>';
}
}
return html;
},
/**
* Checks whether the vnode has any vChildNodes (nodeType of 1, 3 or 8).
*
* @method hasVChildNodes
* @return {Boolean} whether the vnode has any vChildNodes.
* @since 0.0.1
*/
hasVChildNodes: function() {
return this.vChildNodes ? (this.vChildNodes.length>0) : false;
},
/**
* Checks whether the vnode has any vChildren (vChildNodes with nodeType of 1).
*
* @method hasVChildren
* @return {Boolean} whether the vnode has any vChildren.
* @since 0.0.1
*/
hasVChildren: function() {
return this.vChildNodes ? (this.vChildren.length>0) : false;
},
/**
* Checks whether the className is present on the vnode.
*
* @method hasClass
* @param className {String|Array} the className to check for. May be an Array of classNames, which all needs to be present.
* @return {Boolean} whether the className (or classNames) is present on the vnode
* @since 0.0.1
*/
hasClass: function(className) {
var instance = this,
check = function(cl) {
return !!instance.classNames[cl];
};
if (!instance.classNames) {
return false;
}
if (typeof className === STRING) {
return check(className);
}
else if (Array.isArray(className)) {
return className.every(check);
}
return false;
},
/**
* Returns the last child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method lastOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the last child-vnode or null when not present
* @since 0.0.1
*/
lastOfVChildren: function(cssSelector) {
var vChildren = this.vChildren,
found, i, element;
if (vChildren) {
if (!cssSelector) {
return this.vLastElementChild;
}
for (i=vChildren.length-1; !found && (i>=0); i--) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
}
return found;
},
/**
* Checks whether the vnode matches one of the specified selectors. `selectors` can be one, or multiple css-selectors,
* separated by a `comma`. For example: "#myid li.red blue" is one selector, "div.red, div.blue, div.green" are three selectors.
*
* @method matchesSelector
* @param selectors {String} one or more css-selectors
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches one of the selectors
* @since 0.0.1
*/
matchesSelector: function(selectors, relatedVNode) {
var instance = this;
if (instance.nodeType!==1) {
return false;
}
selectors = selectors.split(',');
// we can use Array.some, because there won't be many separated selectoritems,
// so the final invocation won't be delayed much compared to looping
return selectors.some(function(selector) {
return _matchesOneSelector(instance, selector, relatedVNode);
});
},
/**
* Reloads the DOM-attribute into the vnode.
*
* @method matchesSelector
* @param attributeName {String} the name of the attribute to be reloaded.
* @return {Node} the domNode that was reloaded.
* @since 0.0.1
*/
reloadAttr: function(attributeName) {
var instance = this,
domNode = instance.domNode,
attributeValue = domNode._getAttribute(attributeName),
attrs = instance.attrs,
extractStyle, extractClass;
if (instance.nodeType===1) {
attributeValue || (attributeValue='');
if (attributeValue==='') {
delete attrs[attributeName];
// in case of STYLE attributeName --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attributeName --> special treatment
(attributeName===CLASS) && (instance.classNames={});
// in case of ID attributeName --> special treatment
if ((attributeName===ID) && (instance.id)) {
delete nodeids[instance.id];
delete instance.id;
}
}
else {
attrs[attributeName] = attributeValue;
// in case of STYLE attributeName --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(attributeValue);
attributeValue = extractStyle.attrStyle;
if (attributeValue) {
attrs.style = attributeValue;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attributeName --> special treatment
extractClass = extractor.extractClass(attributeValue);
attributeValue = extractClass.attrClass;
if (attributeValue) {
attrs[CLASS] = attributeValue;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (instance.id!==attributeValue) && (delete nodeids[instance.id]);
instance.id = attributeValue;
nodeids[attributeValue] = domNode;
}
}
}
return domNode;
},
/**
* Returns the vnode's style in a serialized form: the way it appears in the dom.
*
* @method serializeStyles
* @return {String} vnode's style
* @since 0.0.1
*/
serializeStyles: function() {
return extractor.serializeStyles(this.styles);
},
/**
* Sets the vnode's and dom-nodes inner HTML.
*
* Syncs with the dom. Can be invoked multiple times without issues.
*
* @method setHTML
* @param content {String} the innerHTML
* @param [suppressItagRender] {Boolean} to suppress Itags from rendering
* @param [allowScripts=false] {Boolean} whether scripts are allowed --> these should be defined with `xscript` instead of `script`
* @chainable
* @since 0.0.1
*/
setHTML: function(content, suppressItagRender, allowScripts) {
var instance = this;
instance._setChildNodes(htmlToVNodes(content, vNodeProto, instance.ns, null, suppressItagRender, allowScripts), suppressItagRender);
return instance;
},
/**
* Syncs the vnode's nodeid (if available) inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom. Can be invoked multiple times without issues.
*
* @method storeId
* @chainable
* @since 0.0.1
*/
storeId: function() {
// store node/vnode inside WeakMap:
var instance = this;
instance.id ? (nodeids[instance.id]=instance.domNode) : (delete nodeids[instance.id]);
return instance;
},
//---- private ------------------------------------------------------------------
_addToTaglist: function() {
var instance = this,
itagList;
if (instance.isItag) {
itagList = DOCUMENT.getItags(); // also reads the dom if the list isn't build yet
instance._data || Object.protectedProp(instance, '_data', {});
if (!instance._data.ce_destroyed && !itagList.contains(instance.domNode)) {
itagList.push(instance.domNode);
}
}
},
/**
* Adds a vnode to the end of the list of vChildNodes.
*
* Syncs with the DOM.
*
* @method _appendChild
* @param VNode {vnode} vnode to append
* @private
* @return {Node} the Node that was appended
* @since 0.0.1
*/
_appendChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
size, noProccessScript, scriptContent;
if ((VNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (VNode.attrs && VNode.attrs.src) ? VNode.attrs.src : VNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
VNode._moveToParent(instance);
instance.domNode._appendChild(domNode);
if (VNode.nodeType===3) {
size = instance.vChildNodes.length;
instance._normalize();
// if the size changed, then the domNode was merged
(size===instance.vChildNodes.length) || (domNode=instance.vChildNodes[instance.vChildNodes.length-1].domNode);
}
if (VNode.nodeType===1) {
VNode._addToTaglist();
VNode._emit(EV_INSERTED);
}
return domNode;
}
},
/**
* Removes the vnode from its parent vChildNodes- and vChildren-list.
*
* Does NOT sync with the dom.
*
* @method _deleteFromParent
* @private
* @chainable
* @since 0.0.1
*/
_deleteFromParent: function() {
var instance = this,
vParent = instance.vParent;
if (vParent && vParent.vChildNodes) {
vParent.vChildNodes.remove(instance);
// force to recalculate the vChildren on a next call:
(instance.nodeType===1) && (vParent._vChildren=null);
}
return instance;
},
/**
* Cleans up (empties) vnode's `_data`
*
* @method _cleanData
* @private
* @chainable
* @since 0.0.1
*/
_cleanData: function() {
var instance = this,
data = instance._data;
data && data.each(
function(value, key) {
delete data[key];
}
);
return instance;
},
/**
* Cleans up (removes) duplicated `style` definitions.
*
* @method _cleanupStyle
* @private
* @chainable
* @since 0.0.1
*/
_cleanupStyle: function() {
var instance = this,
compare = [],
removal = [],
vChildren = instance.vChildren,
len = vChildren.length,
vChild, i, styleContent, vChildInner;
for (i=0; i<len; i++) {
vChild = vChildren[i];
if (vChild.tag==='STYLE') {
vChildInner = vChild.vChildNodes[0];
if (vChildInner) {
styleContent = vChildInner.text;
if (compare.contains(styleContent)) {
removal[removal.length] = vChild;
}
else {
compare[compare.length] = styleContent;
}
}
else {
// empty style-tag
removal[removal.length] = vChild;
}
}
}
removal.forEach(function(vnode) {
instance._removeChild(vnode);
});
},
/**
* Destroys the vnode and all its vnode-vChildNodes.
* Removes it from its vParent.vChildNodes list,
* also removes its definitions inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom.
*
* @method _destroy
* @private
* @chainable
* @since 0.0.1
*/
_destroy: function(silent) {
var instance = this,
vChildNodes = instance.vChildNodes,
len, i, vChildNode, vParent, treeNodes;
if (!instance.destroyed) {
if (!silent) {
// Because we don't want to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
later(function() {
instance._emit(EV_REMOVED, null, null, null, true);
}, 5);
}
Object.protectedProp(instance, 'destroyed', true);
// first: determine the dom-tree, which module `event-dom` needs to determine where the node was before it was destroyed:
treeNodes = [instance];
vParent = instance.vParent;
while (vParent) {
treeNodes[treeNodes.length] = vParent;
vParent = vParent.vParent;
}
// mark all its vChildNodes so we can see if the node is in the DOM
_markRemoved(instance);
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && instance.isItag) {
DOCUMENT._itagList.remove(instance.domNode);
}
instance._scripts && (instance._scripts.length=0);
// The definite cleanup needs to be done after a timeout:
// someone might need to handle the Element when removed (fe to cleanup specific things)
later(function() {
instance._cleanData();
if (instance.nodeType===1) {
// _destroy all its vChildNodes
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && vChildNode._destroy(true);
}
}
}
instance._vChildren = null;
// explicitely set instance.domNode._vnode and instance.domNode to null in order to prevent problems with the GC (we break the circular reference)
delete instance.domNode._vnode;
// if valid id, then _remove the DOMnodeRef from internal hash
instance.id && delete nodeids[instance.id];
}, silent ? 0 : DESTROY_DELAY);
instance._deleteFromParent();
// Do not make domNode `null` --> it could be used even when not in the dom
}
return instance;
},
_emit: function(evt, attribute, newValue, prevValue, destroyEvt) {
/**
* Emitted by every Element that gets inserted.
*
* @event nodeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets removed.
*
* @event noderemove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets its content changed (innerHTML/innerText).
*
* @event nodecontentchange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute inserted.
*
* @event attributeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* </ul>
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute removed.
*
* @event attributeremove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Strings of the attributeNames that are removed
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute changed.
*
* @event attributechange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* <li>prevValue</li>
* </ul>
* @since 0.1
*/
var instance = this,
silent, attrMutations, mutationEvents, mutation, vParent;
if (!DOCUMENT.hasMutationSubs || (instance.nodeType!==1)) {
return;
}
silent = !!DOCUMENT._suppressMutationEvents;
if (!silent && (destroyEvt || !instance.destroyed)) {
// Because we don't wannt to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
mutationEvents = MUTATION_EVENTS.get(instance) || {};
if (attribute) {
attrMutations = mutationEvents[evt] || [];
if (evt===EV_ATTRIBUTE_REMOVED) {
mutation = attribute;
}
else {
mutation = {
attribute: attribute
};
if ((evt===EV_ATTRIBUTE_INSERTED) || (evt===EV_ATTRIBUTE_CHANGED)) {
mutation.newValue = newValue;
}
if ((evt===EV_ATTRIBUTE_CHANGED) && prevValue) {
mutation.prevValue = prevValue;
}
}
attrMutations.push(mutation);
mutationEvents[evt] = attrMutations;
}
else {
mutationEvents[evt] = true;
}
MUTATION_EVENTS.set(instance, mutationEvents);
// now set all parent to have a nodecontentchange:
vParent = instance.vParent;
vParent && vParent._emit(EV_CONTENT_CHANGE);
// in case of removal we need to emit EV_REMOVED for all children right now
// for they will be actually removed silently after a delay of 1 minute
(evt===EV_REMOVED) && _emitDestroyChildren(instance);
if (!BATCH_WILL_RUN) {
BATCH_WILL_RUN = true;
async(function() {
_batchEmit();
});
}
}
return instance;
},
/**
* Inserts `newVNode` before `refVNode`.
*
* Syncs with the DOM.
*
* @method _insertBefore
* @param newVNode {vnode} vnode to insert
* @param refVNode {vnode} The vnode before which newVNode should be inserted.
* @private
* @return {Node} the Node being inserted (equals domNode)
* @since 0.0.1
*/
_insertBefore: function(newVNode, refVNode) {
var instance = this,
domNode = newVNode.domNode,
index = instance.vChildNodes.indexOf(refVNode),
noProccessScript, scriptContent;
if ((newVNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (newVNode.attrs && newVNode.attrs.src) ? newVNode.attrs.src : newVNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
if (index!==-1) {
newVNode._moveToParent(instance, index);
instance.domNode._insertBefore(domNode, refVNode.domNode);
(newVNode.nodeType===3) && instance._normalize();
if (newVNode.nodeType===1) {
newVNode._addToTaglist();
newVNode._emit(EV_INSERTED);
}
return domNode;
}
else {
console.warn('trying to insert before, but no ref-node found. Will append the new node');
return instance._appendChild(newVNode);
}
}
},
/**
* Moves the vnode from its current parent.vChildNodes list towards a new parent vnode at the specified position.
*
* Does NOT sync with the dom.
*
* @method _moveToParent
* @param parentVNode {vnode} the parent-vnode
* @param [index] {Number} the position of the child. When not specified, it will be appended.
* @private
* @chainable
* @since 0.0.1
*/
_moveToParent: function(parentVNode, index) {
var instance = this,
vParent = instance.vParent;
instance._deleteFromParent();
instance.vParent = parentVNode;
parentVNode.vChildNodes || (parentVNode.vChildNodes=[]);
(typeof index==='number') ? parentVNode.vChildNodes.insertAt(instance, index) : (parentVNode.vChildNodes[parentVNode.vChildNodes.length]=instance);
// force to recalculate the vChildren on a next call:
vParent && (instance.nodeType===1) && (vParent._vChildren = null);
// force to recalculate the vChildren on a next call:
parentVNode && (instance.nodeType===1) && (parentVNode._vChildren=null);
return instance;
},
/**
* Removes empty TextNodes and merges following TextNodes inside the vnode.
*
* Syncs with the dom.
*
* @method _normalize
* @private
* @chainable
* @since 0.0.1
*/
_normalize: function() {
var instance = this,
domNode = instance.domNode,
vChildNodes = instance.vChildNodes,
changed = false,
i, preChildNode, vChildNode;
if (!instance._unNormalizable && vChildNodes) {
for (i=vChildNodes.length-1; i>=0; i--) {
vChildNode = vChildNodes[i];
preChildNode = vChildNodes[i-1]; // i will get the value `-1` eventually, which leads into undefined preChildNode
if (vChildNode.nodeType===3) {
if (vChildNode.text==='') {
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
else if (preChildNode && preChildNode.nodeType===3) {
preChildNode.text += vChildNode.text;
preChildNode.domNode.nodeValue = unescapeEntities(preChildNode.text);
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
}
}
}
changed && instance._emit(EV_CONTENT_CHANGE);
return instance;
},
/**
* Makes the vnode `normalizable`. Could be set to `false` when batch-inserting nodes, while `normalizaing` manually at the end.
* Afterwards, you should always reset `normalizable` to true.
*
* @method _normalizable
* @param value {Boolean} whether the vnode should be normalisable.
* @private
* @chainable
* @since 0.0.1
*/
_normalizable: function(value) {
var instance = this;
value ? (delete instance._unNormalizable) : (instance._unNormalizable=true);
return instance;
},
/**
* Prevents MutationObserver from making the dom sync with the vnode.
* Should be used when manipulating the dom from within the vnode itself (to preventing looping)
*
* @method _noSync
* @chainable
* @private
* @since 0.0.1
*/
_noSync: function() {
var instance = this;
if (!instance._nosync) {
instance._nosync = true;
async(function() {
instance._nosync = false;
});
}
return instance;
},
/**
* Removes the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _removeAttr
* @param attributeName {String}
* @private
* @chainable
* @since 0.0.1
*/
_removeAttr: function(attributeName, suppressItagRender) {
var instance = this,
attributeNameSplitted, ns;
if (instance.isItag && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
console.warn('Not allowed to remove the attribute '+attributeName);
return instance;
}
if (instance.attrs[attributeName]!==undefined) {
delete instance.attrs[attributeName];
// in case of STYLE attribute --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attribute --> special treatment
(attributeName===CLASS) && (instance.classNames={});
if (attributeName===ID) {
delete nodeids[instance.id];
delete instance.id;
}
instance._emit(EV_ATTRIBUTE_REMOVED, attributeName);
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
instance.domNode._removeAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName);
}
else {
instance.domNode._removeAttribute(attributeName);
}
}
return instance;
},
/**
* Removes the vnode's child-vnode from its vChildren and the DOM.
*
* Syncs with the DOM.
*
* @method removeChild
* @param VNode {vnode} the child-vnode to remove
* @private
* @since 0.0.1
*/
_removeChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
hadFocus = domNode && domNode.hasFocus && domNode.hasFocus() && (VNode.attrs['fm-lastitem']==='true'),
parentVNode = VNode.vParent;
VNode._destroy();
_tryRemoveDomNode(instance.domNode, VNode.domNode);
instance._normalize();
// now, reset the focus on focusmanager when needed:
if (hadFocus) {
while (parentVNode && !parentVNode.attrs['fm-manage']) {
parentVNode = parentVNode.vParent;
}
parentVNode && parentVNode.domNode.focus();
}
},
/**
* Replaces the current vnode at the parent.vChildNode list by `newVNode`
*
* Does NOT sync with the dom.
*
* @method _replaceAtParent
* @param newVNode {Object} the new vnode which should take over the place of the current vnode
* @private
* @chainable
* @since 0.0.1
*/
_replaceAtParent: function(newVNode) {
var instance = this,
vParent = instance.vParent,
vChildNodes, index;
if (vParent && (vChildNodes=vParent.vChildNodes)) {
index = vChildNodes.indexOf(instance);
// force to recalculate the vChildren on a next call:
((instance.nodeType===1) || (newVNode.nodeType===1)) && (instance.vParent._vChildren=null);
vChildNodes[index] = newVNode;
}
return instance._destroy();
},
/**
* Sets the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _setAttr
* @param attributeName {String}
* @param value {String} the value for the attributeName
* @param [force=false] {Boolean} force the attribute to be set, even if restrictions would deny it
* @private
* @chainable
* @since 0.0.1
*/
_setAttr: function(attributeName, value, force, suppressItagRender) {
var instance = this,
extractStyle, extractClass,
attrs = instance.attrs,
prevVal = attrs[attributeName],
domNode = instance.domNode,
attributeNameSplitted, ns;
if (instance.isItag && !force && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
if (prevVal!=value) {
console.warn('Not allowed to set the attribute '+attributeName);
}
return instance;
}
// don't check by !== --> value isn't parsed into a String yet
if (prevVal && ((value===undefined) || (value===null))) {
instance._removeAttr(attributeName, suppressItagRender);
return instance;
}
// attribute-values are always Strings:
value = String(value);
// attribute-values will be stored without " or '
value = value.replace(/"/g, '"').replace(/'/g, "'");
if (prevVal!=value) {
attrs[attributeName] = value;
// in case of STYLE attribute --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(value);
value = extractStyle.attrStyle;
if (value) {
attrs.style = value;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attribute --> special treatment
extractClass = extractor.extractClass(value);
value = extractClass.attrClass;
if (value) {
attrs[CLASS] = value;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (delete nodeids[instance.id]);
instance.id = value;
nodeids[value] = domNode;
}
instance._emit(prevVal ? EV_ATTRIBUTE_CHANGED : EV_ATTRIBUTE_INSERTED, attributeName, value, prevVal);
// when set in the dom --> quotes need to be set as "
value = value.replace(/"/g, '"');
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
domNode._setAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName, value);
}
else {
domNode._setAttribute(attributeName, value);
}
}
return instance;
},
/**
* Redefines the attributes of both the vnode as well as its related dom-node. The new
* definition replaces any previous attributes (without touching unmodified attributes).
* the `is` attribute cannot be changed for itags.
*
* Syncs the new vnode's attributes with the dom.
*
* @method _setAttrs
* @param newAttrs {Object|Array} the new attributes to be set
* @private
* @chainable
* @since 0.0.1
*/
_setAttrs: function(newAttrs, suppressItagRender) {
// does sync the DOM
var instance = this,
attrsObj, attr, attrs, i, key, keys, len, value;
if (instance.nodeType!==1) {
return;
}
instance._noSync();
attrs = instance.attrs;
attrs.id && (delete nodeids[attrs.id]);
if (Object.isObject(newAttrs)) {
attrsObj = newAttrs;
}
else {
attrsObj = {};
len = newAttrs.length;
for (i=0; i<len; i++) {
attr = newAttrs[i];
attrsObj[attr.name] = attr.value;
}
}
if (instance.isItag && !suppressItagRender) {
if (attrs.is) {
attrsObj.is = attrs.is;
}
else {
delete attrsObj.is;
delete attrsObj.Is;
delete attrsObj.iS;
delete attrsObj.IS;
}
}
// first _remove the attributes that are no longer needed.
// quickest way for object iteration: http://jsperf.com/object-keys-iteration/20
keys = Object.keys(attrs);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
attrsObj[key] || instance._removeAttr(key, suppressItagRender);
}
// next: every attribute that differs: redefine
keys = Object.keys(attrsObj);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
value = attrsObj[key];
(attrs[key]===value) || instance._setAttr(key, value, suppressItagRender);
}
return instance;
},
/**
* Redefines the childNodes of both the vnode as well as its related dom-node. The new
* definition replaces any previous nodes. (without touching unmodified nodes).
*
* Syncs the new vnode's childNodes with the dom.
*
* @method _setChildNodes
* @param newVChildNodes {Array} array with vnodes which represent the new childNodes
* @private
* @chainable
* @since 0.0.1
*/
_setChildNodes: function(newVChildNodes, suppressItagRender, removeSystemElements) {
// does sync the DOM
var instance = this,
vChildNodes = instance.vChildNodes || [],
domNode = instance.domNode,
forRemoval = [],
i, oldChild, newChild, newLength, len, len2, childDomNode, nodeswitch, bkpAttrs, cleanupStyle,
process, bkpChildNodes, needNormalize, prevSuppress, scriptContent, _scripts, scriptLen, j;
instance._noSync();
// first: reset ._vChildren --> by making it empty, its getter will refresh its list on a next call
instance._vChildren = null;
// if newVChildNodes is undefined, then we assume it to be empty --> an empty array
newVChildNodes || (newVChildNodes=[]);
// quickest way to loop through array is by using for loops: http://jsperf.com/array-foreach-vs-for-loop/5
len = vChildNodes.length;
// we need to add the systemNodes -if any- to the new newVChildNodes: they should be retained.
// SystemNodes are always places at the beginning, so they won't get reshuffled:
if (!removeSystemElements) {
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
if (oldChild._systemNode) {
newVChildNodes.insertAt(oldChild, i);
}
else {
break;
}
}
}
newLength = newVChildNodes.length;
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
childDomNode = oldChild.domNode;
if (i < newLength) {
newChild = newVChildNodes[i];
newChild.vParent || (newChild.vParent=instance);
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
/*jshint boss:true */
switch (nodeswitch=NODESWITCH[oldChild.nodeType][newChild.nodeType]) {
/*jshint boss:false */
case 1: // oldNodeType==Element, newNodeType==Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
if ((oldChild.tag!==newChild.tag) ||
((oldChild.tag===newChild.tag) && oldChild.isItag && (oldChild.attrs.is!==newChild.attrs.is))) {
// new tag --> completely replace
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
/*jshint proto:true */
oldChild.isItag && oldChild.domNode.destroyUI && oldChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
newChild.attrs = {}; // reset to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
oldChild._replaceAtParent(newChild);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
else {
// same tag --> only update what is needed
// NOTE: when this._unchangableAttrs exists, an itag-element syncs its UI -->
if (oldChild._data) {
// we might need to set the class `itag-rendered` when the attributeData says so:
// this happens when an itag gets refreshed with an unrendered definition
if (oldChild._data.itagRendered && !newChild.hasClass('itag-rendered')) {
newChild.classNames['itag-rendered'] = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' '+'itag-rendered';
}
else {
newChild.attrs[CLASS] = 'itag-rendered';
}
}
// we might need to set the class `focussed` when the attributeData says so:
// this happens when an itag gets rerendered: its renderFn doesn't know if any elements
// were focussed
if (oldChild._data.focussed && !newChild.hasClass('focussed')) {
newChild.classNames.focussed = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' ' + 'focussed';
}
else {
newChild.attrs[CLASS] = 'focussed';
}
}
if (oldChild._data['fm-tabindex']) {
// node has the tabindex set by the focusmanager,
// but that info might got lost with re-rendering of the new element
newChild.attrs.tabindex = '0';
}
}
if (oldChild.isItag) {
prevSuppress = DOCUMENT._suppressMutationEvents || false;
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
/*jshint proto:true */
newChild.domNode.destroyUI && newChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
oldChild._setAttrs(newChild.attrs, suppressItagRender);
newChild._destroy(true); // destroy through the vnode and removing from DOCUMENT._itagList
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(prevSuppress);
}
else {
oldChild._setAttrs(newChild.attrs, suppressItagRender);
// next: sync the vChildNodes:
oldChild._setChildNodes(newChild.vChildNodes, suppressItagRender);
}
// reset ref. to the domNode, for it might have been changed by newChild:
oldChild.id && (nodeids[oldChild.id]=childDomNode);
newVChildNodes[i] = oldChild;
}
}
break;
case 2: // oldNodeType==Element, newNodeType==TextNode
// case2 and case3 should be treated the same
case 3: // oldNodeType==Element, newNodeType==Comment
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
oldChild._replaceAtParent(newChild);
instance._emit(EV_CONTENT_CHANGE);
break;
case 4: // oldNodeType==TextNode, newNodeType==Element
// case4 and case7 should be treated the same
case 7: // oldNodeType==Comment, newNodeType==Element
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
// oldChild.isVoid = newChild.isVoid;
// delete oldChild.text;
instance._emit(EV_CONTENT_CHANGE);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
break;
case 5: // oldNodeType==TextNode, newNodeType==TextNode
// case5 and case9 should be treated the same
case 9: // oldNodeType==Comment, newNodeType==Comment
if (oldChild.text!==newChild.text) {
oldChild.text = newChild.text;
oldChild.domNode.nodeValue = unescapeEntities(newChild.text);
instance._emit(EV_CONTENT_CHANGE);
}
newVChildNodes[i] = oldChild;
break;
case 6: // oldNodeType==TextNode, newNodeType==Comment
// case6 and case8 should be treated the same
case 8: // oldNodeType==Comment, newNodeType==TextNode
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = oldChild.vParent;
instance._emit(EV_CONTENT_CHANGE);
}
if ((nodeswitch===2) || (nodeswitch===5) || (nodeswitch===8)) {
needNormalize = true;
}
}
else {
// _remove previous definition
_tryRemoveDomNode(domNode, oldChild.domNode);
// the oldChild needs to be removed, however, this cannot be done right now, for it would effect the loop
// so we store it inside a hash to remove it later
forRemoval[forRemoval.length] = oldChild;
}
}
// now definitely remove marked childNodes:
len2 = forRemoval.length;
for (i=0; i<len2; i++) {
forRemoval[i]._destroy();
}
// now we add all new vChildNodes that go beyond `len`:
for (i = len; i < newLength; i++) {
newChild = newVChildNodes[i];
newChild.vParent = instance;
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
switch (newChild.nodeType) {
case 1: // Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
domNode._appendChild(newChild.domNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
break;
case 3: // TextNode
needNormalize = true;
// we need to break through --> no `break`
/* falls through */
default: // TextNode or CommentNode
// newChild.domNode.nodeValue = newChild.text;
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
domNode._appendChild(newChild.domNode);
instance._emit(EV_CONTENT_CHANGE);
}
newChild.storeId();
}
instance.vChildNodes = newVChildNodes;
needNormalize && instance._normalize();
cleanupStyle && instance._cleanupStyle();
return instance;
},
_setUnchangableAttrs: function(unchangableObj) {
this._unchangableAttrs = unchangableObj;
}
};
//---- properties ------------------------------------------------------------------
/**
* A hash of all the `attributes` of the vnode's representing dom-node.
*
* @property attrs
* @type Object
* @since 0.0.1
*/
/**
* Hash with all the classes of the vnode. Every class represents a key, all values are set `true`.
*
* @property classNames
* @type Object
* @since 0.0.1
*/
/**
* The `id` of the vnode's representing dom-node (if any).
*
* @property id
* @type String
* @since 0.0.1
*/
/**
* Tells whether tag is a void Element. Examples are: `br`, `img` and `input`. Non-void Elements are f.e. `div` and `table`.
* For TextNodes and CommentNodes, this property is `undefined`.
*
* @property isVoid
* @type Boolean
* @since 0.0.1
*/
/**
* The `nodeType` of the vnode's representing dom-node (1===ElementNode, 3===TextNode, 8===CommentNode).
*
* @property nodeType
* @type Number
* @since 0.0.1
*/
/**
* The `tag` of the vnode's representing dom-node (allways uppercase).
*
* @property tag
* @type String
* @since 0.0.1
*/
/**
* The `content` of the vnode's representing dom-node, in case it is a TextNode or CommentNode.
* Equals dom-node.nodeValue.
*
* Is `undefined` for ElementNodes.
*
* @property text
* @type String
* @since 0.0.1
*/
/**
* Hash with all the childNodes (vnodes). vChildNodes are any kind of vnodes (nodeType===1, 3 or 8)
*
* @property vChildNodes
* @type Array
* @since 0.0.1
*/
/**
* The underlying `dom-node` that the vnode represents.
*
* @property domNode
* @type domNode
* @since 0.0.1
*/
/**
* vnode's parentNode (defined as a vnode itself).
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
Object.defineProperties(vNodeProto, {
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property innerHTML
* @type String
* @since 0.0.1
*/
innerHTML: {
get: function() {
return this.getHTML();
},
set: function(v) {
this.setHTML(v);
}
},
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property nodeValue
* @type String
* @since 0.0.1
*/
nodeValue: {
get: function() {
var instance = this;
return ((instance.nodeType===3) || (instance.nodeType===8)) ? instance.text : null;
},
set: function(v) {
var instance = this,
newTextContent, prevTextContent;
if ((instance.nodeType===3) || (instance.nodeType===8)) {
prevTextContent = instance.domNode.textContent;
instance.domNode.textContent = v;
// set .text AFTER the dom-node is updated --> the content might be escaped!
newTextContent = instance.text = instance.domNode.textContent;
(newTextContent!==prevTextContent) && instance._emit(EV_CONTENT_CHANGE);
}
}
},
/**
* Gets or sets the outerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property outerHTML
* @type String
* @since 0.0.1
*/
outerHTML: {
get: function() {
return this.getOuterHTML();
},
set: function(v) {
var instance = this,
vParent = instance.vParent,
id = instance.attrs.id,
vnode, vnodes, bkpAttrs, bkpChildNodes, i, len, vChildNodes, isLastChildNode, index, refDomNode;
if ((instance.nodeType!==1) || !vParent) {
return;
}
instance._noSync();
vChildNodes = vParent.vChildNodes;
index = vChildNodes.indexOf(instance);
isLastChildNode = (index===(vChildNodes.length-1));
isLastChildNode || (refDomNode=vChildNodes[index+1].domNode);
vnodes = htmlToVNodes(v, vNodeProto, vParent.ns, vParent);
len = vnodes.length;
if (len>0) {
// the first vnode will replace the current instance:
vnode = vnodes[0];
if (vnode.nodeType===1) {
if (vnode.tag!==instance.tag) {
// new tag --> completely replace
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
id && (delete nodeids[id]);
vnode.attrs = {}; // reset to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
// vnode.attrs = bkpAttrs;
// vnode.vChildNodes = bkpChildNodes;
vnode.id && (nodeids[vnode.id]=vnode.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
else {
instance._setAttrs(vnode.attrs);
instance._setChildNodes(vnode.vChildNodes);
}
}
else {
id && (delete nodeids[id]);
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
}
for (i=1; i<len; i++) {
vnode = vnodes[i];
switch (vnode.nodeType) {
case 1: // Element
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
vnode.attrs = {}; // reset, to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._insertBefore(vnode.domNode, refDomNode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
break;
default: // TextNode or CommentNode
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._appendChild(vnode.domNode, refDomNode);
}
vnode.storeId();
vnode._moveToParent(vParent, index+i);
}
}
},
/**
* Gets or sets the innerContent of the Node as plain text.
*
* The setter syncs with the DOM.
*
* @property textContent
* @type String
* @since 0.0.1
*/
textContent: {
get: function() {
var instance = this,
text = '',
vChildNodes = instance.vChildNodes,
len, i, vChildNode;
if (instance.nodeType===1) {
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
text += (vChildNode.nodeType===3) ? vChildNode.text : ((vChildNode.nodeType===1) ? vChildNode.textContent : '');
}
}
else {
text = instance.text;
}
return text;
},
set: function(v) {
var vnode = Object.create(vNodeProto);
vnode.domNode = DOCUMENT.createTextNode(v);
// create circular reference:
vnode.domNode._vnode = vnode;
vnode.nodeType = 3;
vnode.text = vnode.domNode.textContent;
this._setChildNodes([vnode]);
}
},
/**
* Hash with all the children (vnodes). vChildren are vnodes that have a representing dom-node that is an HtmlElement (nodeType===1)
*
* @property vChildren
* @type Array
* @since 0.0.1
*/
vChildren: {
get: function() {
var instance = this,
children = instance._vChildren,
vChildNode, vChildNodes, i, len;
vChildNodes = instance.vChildNodes;
if (vChildNodes && !children) {
children = instance._vChildren = [];
len = vChildNodes.length;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
(vChildNode.nodeType===1) && (children[children.length]=vChildNode);
}
}
children || (children = instance._vChildren = []);
return children;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirst
* @type vnode
* @since 0.0.1
*/
vFirst: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstChild;
}
},
/**
* Reference to the first vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirstChild
* @type vnode
* @since 0.0.1
*/
vFirstChild: {
get: function() {
return (this.vChildNodes && this.vChildNodes[0]) || null;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vFirstElement
* @type vnode
* @since 0.0.1
*/
vFirstElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstElementChild;
}
},
/**
* Reference to the first vChild, where the related dom-node an Element (nodeType===1).
*
* @property vFirstElementChild
* @type vnode
* @since 0.0.1
*/
vFirstElementChild: {
get: function() {
return this.vChildren[0] || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLast
* @type vnode
* @since 0.0.1
*/
vLast: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastChild;
}
},
/**
* Reference to the last vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLastChild
* @type vnode
* @since 0.0.1
*/
vLastChild: {
get: function() {
var vChildNodes = this.vChildNodes;
return (vChildNodes && vChildNodes[vChildNodes.length-1]) || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vLastElement
* @type vnode
* @since 0.0.1
*/
vLastElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastElementChild;
}
},
/**
* Reference to the last vChild, where the related dom-node an Element (nodeType===1).
*
* @property vLastElementChild
* @type vnode
* @since 0.0.1
*/
vLastElementChild: {
get: function() {
var vChildren = this.vChildren;
return vChildren[vChildren.length-1] || null;
}
},
/**
* the Parent vnode
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
/**
* Reference to the next of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vNext
* @type vnode
* @since 0.0.1
*/
vNext: {
get: function() {
return _findNodeSibling(this, true);
}
},
/**
* Reference to the next of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vNextElement
* @type vnode
* @since 0.0.1
*/
vNextElement: {
get: function() {
return _findElementSibling(this, true);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vPrevious
* @type vnode
* @since 0.0.1
*/
vPrevious: {
get: function() {
return _findNodeSibling(this);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vPreviousElement
* @type vnode
* @since 0.0.1
*/
vPreviousElement: {
get: function() {
return _findElementSibling(this);
}
}
});
return vNodeProto;
};
},{"./attribute-extractor.js":3941,"./html-parser.js":3945,"./vdom-ns.js":3947,"js-ext/extra/hashmap.js":3899,"js-ext/extra/lightmap.js":3900,"js-ext/lib/array.js":3901,"js-ext/lib/object.js":3902,"js-ext/lib/string.js":3904,"polyfill":3921,"utils/lib/timers.js":3924}],3949:[function(require,module,exports){
arguments[4][100][0].apply(exports,arguments)
},{"./partials/extend-document.js":3943,"./partials/extend-element.js":3944,"./partials/node-parser.js":3946,"js-ext/extra/hashmap.js":3899,"js-ext/lib/object.js":3902,"utils/lib/timers.js":3924}],3950:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3951,"./lib/timers.js":3952}],3951:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4265,"polyfill/polyfill-base.js":3955}],3952:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3955}],3953:[function(require,module,exports){
module.exports=require(14)
},{}],3954:[function(require,module,exports){
module.exports=require(15)
},{}],3955:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3953,"./lib/window.console.js":3954}],3956:[function(require,module,exports){
module.exports=require(737)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],3957:[function(require,module,exports){
module.exports=require(738)
},{"./css/focusmanager.css":3956,"event-mobile":3958,"js-ext/extra/hashmap.js":4061,"js-ext/lib/object.js":4062,"node-plugin":4072,"polyfill":4244,"utils":4245,"window-ext":4251}],3958:[function(require,module,exports){
module.exports=require(634)
},{"./lib/hammer-2.0.4.js":3959,"event-dom":3960}],3959:[function(require,module,exports){
module.exports=require(635)
},{"utils":4055}],3960:[function(require,module,exports){
module.exports=require(267)
},{"event":3964,"js-ext/extra/hashmap.js":3980,"js-ext/lib/array.js":3981,"js-ext/lib/object.js":3982,"js-ext/lib/string.js":3983,"polyfill/polyfill-base.js":3995,"utils":3996,"vdom":4054}],3961:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":3966,"js-ext/lib/object.js":3967,"polyfill/polyfill-base.js":3979}],3962:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":3961}],3963:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":3961,"js-ext/extra/classes.js":3965,"js-ext/lib/object.js":3967}],3964:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":3961,"./event-emitter.js":3962,"./event-listener.js":3963}],3965:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":3967,"js-ext/extra/hashmap.js":3966,"polyfill/polyfill-base.js":3970}],3966:[function(require,module,exports){
module.exports=require(4)
},{}],3967:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3966,"polyfill/polyfill-base.js":3970,"utils":3971}],3968:[function(require,module,exports){
module.exports=require(14)
},{}],3969:[function(require,module,exports){
module.exports=require(15)
},{}],3970:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3968,"./lib/window.console.js":3969}],3971:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3972,"./lib/timers.js":3973}],3972:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3966,"polyfill/polyfill-base.js":3976}],3973:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3976}],3974:[function(require,module,exports){
module.exports=require(14)
},{}],3975:[function(require,module,exports){
module.exports=require(15)
},{}],3976:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3974,"./lib/window.console.js":3975}],3977:[function(require,module,exports){
module.exports=require(14)
},{}],3978:[function(require,module,exports){
module.exports=require(15)
},{}],3979:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3977,"./lib/window.console.js":3978}],3980:[function(require,module,exports){
module.exports=require(4)
},{}],3981:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":3986}],3982:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":3980,"polyfill/polyfill-base.js":3986,"utils":3987}],3983:[function(require,module,exports){
module.exports=require(29)
},{}],3984:[function(require,module,exports){
module.exports=require(14)
},{}],3985:[function(require,module,exports){
module.exports=require(15)
},{}],3986:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3984,"./lib/window.console.js":3985}],3987:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3988,"./lib/timers.js":3989}],3988:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3980,"polyfill/polyfill-base.js":3992}],3989:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":3992}],3990:[function(require,module,exports){
module.exports=require(14)
},{}],3991:[function(require,module,exports){
module.exports=require(15)
},{}],3992:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3990,"./lib/window.console.js":3991}],3993:[function(require,module,exports){
module.exports=require(14)
},{}],3994:[function(require,module,exports){
module.exports=require(15)
},{}],3995:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3993,"./lib/window.console.js":3994}],3996:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":3997,"./lib/timers.js":3998}],3997:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3980,"polyfill/polyfill-base.js":4001}],3998:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4001}],3999:[function(require,module,exports){
module.exports=require(14)
},{}],4000:[function(require,module,exports){
module.exports=require(15)
},{}],4001:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":3999,"./lib/window.console.js":4000}],4002:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],4003:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":4007,"js-ext/extra/hashmap.js":4004,"polyfill/polyfill-base.js":4013}],4004:[function(require,module,exports){
module.exports=require(4)
},{}],4005:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":4006,"../lib/object.js":4007,"./classes.js":4003,"js-ext/extra/hashmap.js":4004,"polyfill/lib/weakmap.js":4011}],4006:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":4013}],4007:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4004,"polyfill/polyfill-base.js":4013,"utils":4014}],4008:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":4013}],4009:[function(require,module,exports){
module.exports=require(29)
},{}],4010:[function(require,module,exports){
module.exports=require(14)
},{}],4011:[function(require,module,exports){
module.exports=require(57)
},{}],4012:[function(require,module,exports){
module.exports=require(15)
},{}],4013:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4010,"./lib/window.console.js":4012}],4014:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4015,"./lib/timers.js":4016}],4015:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4004,"polyfill/polyfill-base.js":4019}],4016:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4019}],4017:[function(require,module,exports){
module.exports=require(14)
},{}],4018:[function(require,module,exports){
module.exports=require(15)
},{}],4019:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4017,"./lib/window.console.js":4018}],4020:[function(require,module,exports){
module.exports=require(66)
},{}],4021:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":4020}],4022:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":4020}],4023:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":4020}],4024:[function(require,module,exports){
module.exports=require(14)
},{}],4025:[function(require,module,exports){
module.exports=require(15)
},{}],4026:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4024,"./lib/window.console.js":4025}],4027:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4028,"./lib/timers.js":4029}],4028:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4004,"polyfill/polyfill-base.js":4032}],4029:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4032}],4030:[function(require,module,exports){
module.exports=require(14)
},{}],4031:[function(require,module,exports){
module.exports=require(15)
},{}],4032:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4030,"./lib/window.console.js":4031}],4033:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":4034}],4034:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":4035,"js-ext/lib/object.js":4036}],4035:[function(require,module,exports){
module.exports=require(4)
},{}],4036:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4035,"polyfill/polyfill-base.js":4039,"utils":4040}],4037:[function(require,module,exports){
module.exports=require(14)
},{}],4038:[function(require,module,exports){
module.exports=require(15)
},{}],4039:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4037,"./lib/window.console.js":4038}],4040:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4041,"./lib/timers.js":4042}],4041:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4035,"polyfill/polyfill-base.js":4045}],4042:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4045}],4043:[function(require,module,exports){
module.exports=require(14)
},{}],4044:[function(require,module,exports){
module.exports=require(15)
},{}],4045:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4043,"./lib/window.console.js":4044}],4046:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":4004,"js-ext/lib/object.js":4007,"js-ext/lib/string.js":4009,"polyfill":4026,"polyfill/extra/transition.js":4021,"polyfill/extra/vendorCSS.js":4023}],4047:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":4004,"js-ext/lib/object.js":4007,"polyfill":4026}],4048:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":4004,"js-ext/lib/object.js":4007,"js-ext/lib/string.js":4009,"polyfill":4026}],4049:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":4002,"./attribute-extractor.js":4046,"./element-array.js":4047,"./html-parser.js":4050,"./node-parser.js":4051,"./vdom-ns.js":4052,"./vnode.js":4053,"js-ext/extra/hashmap.js":4004,"js-ext/lib/object.js":4007,"js-ext/lib/promise.js":4008,"js-ext/lib/string.js":4009,"polyfill":4026,"polyfill/extra/transition.js":4021,"polyfill/extra/transitionend.js":4022,"polyfill/extra/vendorCSS.js":4023,"utils":4027,"window-ext":4033}],4050:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":4046,"./vdom-ns.js":4052,"js-ext/extra/hashmap.js":4004,"js-ext/lib/object.js":4007,"polyfill":4026}],4051:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":4046,"./vdom-ns.js":4052,"./vnode.js":4053,"js-ext/extra/hashmap.js":4004,"js-ext/lib/object.js":4007,"polyfill":4026}],4052:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":4004,"js-ext/lib/object.js":4007,"polyfill":4026}],4053:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":4046,"./html-parser.js":4050,"./vdom-ns.js":4052,"js-ext/extra/hashmap.js":4004,"js-ext/extra/lightmap.js":4005,"js-ext/lib/array.js":4006,"js-ext/lib/object.js":4007,"js-ext/lib/string.js":4009,"polyfill":4026,"utils/lib/timers.js":4029}],4054:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":4048,"./partials/extend-element.js":4049,"./partials/node-parser.js":4051,"js-ext/extra/hashmap.js":4004,"js-ext/lib/object.js":4007,"utils/lib/timers.js":4029}],4055:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4056,"./lib/timers.js":4057}],4056:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4061,"polyfill/polyfill-base.js":4060}],4057:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4060}],4058:[function(require,module,exports){
module.exports=require(14)
},{}],4059:[function(require,module,exports){
module.exports=require(15)
},{}],4060:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4058,"./lib/window.console.js":4059}],4061:[function(require,module,exports){
module.exports=require(4)
},{}],4062:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4061,"polyfill/polyfill-base.js":4065,"utils":4066}],4063:[function(require,module,exports){
module.exports=require(14)
},{}],4064:[function(require,module,exports){
module.exports=require(15)
},{}],4065:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4063,"./lib/window.console.js":4064}],4066:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4067,"./lib/timers.js":4068}],4067:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4061,"polyfill/polyfill-base.js":4071}],4068:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4071}],4069:[function(require,module,exports){
module.exports=require(14)
},{}],4070:[function(require,module,exports){
module.exports=require(15)
},{}],4071:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4069,"./lib/window.console.js":4070}],4072:[function(require,module,exports){
arguments[4][5][0].apply(exports,arguments)
},{"event-dom":4073,"js-ext/extra/classes.js":4168,"js-ext/extra/hashmap.js":4169,"js-ext/lib/object.js":4170,"js-ext/lib/promise.js":4171,"js-ext/lib/string.js":4172,"polyfill":4184,"utils/lib/timers.js":4185,"vdom":4241}],4073:[function(require,module,exports){
module.exports=require(267)
},{"event":4077,"js-ext/extra/hashmap.js":4093,"js-ext/lib/array.js":4094,"js-ext/lib/object.js":4095,"js-ext/lib/string.js":4096,"polyfill/polyfill-base.js":4108,"utils":4109,"vdom":4167}],4074:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":4079,"js-ext/lib/object.js":4080,"polyfill/polyfill-base.js":4092}],4075:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":4074}],4076:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":4074,"js-ext/extra/classes.js":4078,"js-ext/lib/object.js":4080}],4077:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":4074,"./event-emitter.js":4075,"./event-listener.js":4076}],4078:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":4080,"js-ext/extra/hashmap.js":4079,"polyfill/polyfill-base.js":4083}],4079:[function(require,module,exports){
module.exports=require(4)
},{}],4080:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4079,"polyfill/polyfill-base.js":4083,"utils":4084}],4081:[function(require,module,exports){
module.exports=require(14)
},{}],4082:[function(require,module,exports){
module.exports=require(15)
},{}],4083:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4081,"./lib/window.console.js":4082}],4084:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4085,"./lib/timers.js":4086}],4085:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4079,"polyfill/polyfill-base.js":4089}],4086:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4089}],4087:[function(require,module,exports){
module.exports=require(14)
},{}],4088:[function(require,module,exports){
module.exports=require(15)
},{}],4089:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4087,"./lib/window.console.js":4088}],4090:[function(require,module,exports){
module.exports=require(14)
},{}],4091:[function(require,module,exports){
module.exports=require(15)
},{}],4092:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4090,"./lib/window.console.js":4091}],4093:[function(require,module,exports){
module.exports=require(4)
},{}],4094:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":4099}],4095:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4093,"polyfill/polyfill-base.js":4099,"utils":4100}],4096:[function(require,module,exports){
module.exports=require(29)
},{}],4097:[function(require,module,exports){
module.exports=require(14)
},{}],4098:[function(require,module,exports){
module.exports=require(15)
},{}],4099:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4097,"./lib/window.console.js":4098}],4100:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4101,"./lib/timers.js":4102}],4101:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4093,"polyfill/polyfill-base.js":4105}],4102:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4105}],4103:[function(require,module,exports){
module.exports=require(14)
},{}],4104:[function(require,module,exports){
module.exports=require(15)
},{}],4105:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4103,"./lib/window.console.js":4104}],4106:[function(require,module,exports){
module.exports=require(14)
},{}],4107:[function(require,module,exports){
module.exports=require(15)
},{}],4108:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4106,"./lib/window.console.js":4107}],4109:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4110,"./lib/timers.js":4111}],4110:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4093,"polyfill/polyfill-base.js":4114}],4111:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4114}],4112:[function(require,module,exports){
module.exports=require(14)
},{}],4113:[function(require,module,exports){
module.exports=require(15)
},{}],4114:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4112,"./lib/window.console.js":4113}],4115:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],4116:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":4120,"js-ext/extra/hashmap.js":4117,"polyfill/polyfill-base.js":4126}],4117:[function(require,module,exports){
module.exports=require(4)
},{}],4118:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":4119,"../lib/object.js":4120,"./classes.js":4116,"js-ext/extra/hashmap.js":4117,"polyfill/lib/weakmap.js":4124}],4119:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":4126}],4120:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4117,"polyfill/polyfill-base.js":4126,"utils":4127}],4121:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":4126}],4122:[function(require,module,exports){
module.exports=require(29)
},{}],4123:[function(require,module,exports){
module.exports=require(14)
},{}],4124:[function(require,module,exports){
module.exports=require(57)
},{}],4125:[function(require,module,exports){
module.exports=require(15)
},{}],4126:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4123,"./lib/window.console.js":4125}],4127:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4128,"./lib/timers.js":4129}],4128:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4117,"polyfill/polyfill-base.js":4132}],4129:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4132}],4130:[function(require,module,exports){
module.exports=require(14)
},{}],4131:[function(require,module,exports){
module.exports=require(15)
},{}],4132:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4130,"./lib/window.console.js":4131}],4133:[function(require,module,exports){
module.exports=require(66)
},{}],4134:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":4133}],4135:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":4133}],4136:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":4133}],4137:[function(require,module,exports){
module.exports=require(14)
},{}],4138:[function(require,module,exports){
module.exports=require(15)
},{}],4139:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4137,"./lib/window.console.js":4138}],4140:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4141,"./lib/timers.js":4142}],4141:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4117,"polyfill/polyfill-base.js":4145}],4142:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4145}],4143:[function(require,module,exports){
module.exports=require(14)
},{}],4144:[function(require,module,exports){
module.exports=require(15)
},{}],4145:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4143,"./lib/window.console.js":4144}],4146:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":4147}],4147:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":4148,"js-ext/lib/object.js":4149}],4148:[function(require,module,exports){
module.exports=require(4)
},{}],4149:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4148,"polyfill/polyfill-base.js":4152,"utils":4153}],4150:[function(require,module,exports){
module.exports=require(14)
},{}],4151:[function(require,module,exports){
module.exports=require(15)
},{}],4152:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4150,"./lib/window.console.js":4151}],4153:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4154,"./lib/timers.js":4155}],4154:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4148,"polyfill/polyfill-base.js":4158}],4155:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4158}],4156:[function(require,module,exports){
module.exports=require(14)
},{}],4157:[function(require,module,exports){
module.exports=require(15)
},{}],4158:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4156,"./lib/window.console.js":4157}],4159:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":4117,"js-ext/lib/object.js":4120,"js-ext/lib/string.js":4122,"polyfill":4139,"polyfill/extra/transition.js":4134,"polyfill/extra/vendorCSS.js":4136}],4160:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":4117,"js-ext/lib/object.js":4120,"polyfill":4139}],4161:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":4117,"js-ext/lib/object.js":4120,"js-ext/lib/string.js":4122,"polyfill":4139}],4162:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":4115,"./attribute-extractor.js":4159,"./element-array.js":4160,"./html-parser.js":4163,"./node-parser.js":4164,"./vdom-ns.js":4165,"./vnode.js":4166,"js-ext/extra/hashmap.js":4117,"js-ext/lib/object.js":4120,"js-ext/lib/promise.js":4121,"js-ext/lib/string.js":4122,"polyfill":4139,"polyfill/extra/transition.js":4134,"polyfill/extra/transitionend.js":4135,"polyfill/extra/vendorCSS.js":4136,"utils":4140,"window-ext":4146}],4163:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":4159,"./vdom-ns.js":4165,"js-ext/extra/hashmap.js":4117,"js-ext/lib/object.js":4120,"polyfill":4139}],4164:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":4159,"./vdom-ns.js":4165,"./vnode.js":4166,"js-ext/extra/hashmap.js":4117,"js-ext/lib/object.js":4120,"polyfill":4139}],4165:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":4117,"js-ext/lib/object.js":4120,"polyfill":4139}],4166:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":4159,"./html-parser.js":4163,"./vdom-ns.js":4165,"js-ext/extra/hashmap.js":4117,"js-ext/extra/lightmap.js":4118,"js-ext/lib/array.js":4119,"js-ext/lib/object.js":4120,"js-ext/lib/string.js":4122,"polyfill":4139,"utils/lib/timers.js":4142}],4167:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":4161,"./partials/extend-element.js":4162,"./partials/node-parser.js":4164,"js-ext/extra/hashmap.js":4117,"js-ext/lib/object.js":4120,"utils/lib/timers.js":4142}],4168:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":4170,"js-ext/extra/hashmap.js":4169,"polyfill/polyfill-base.js":4175}],4169:[function(require,module,exports){
module.exports=require(4)
},{}],4170:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4169,"polyfill/polyfill-base.js":4175,"utils":4176}],4171:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":4175}],4172:[function(require,module,exports){
module.exports=require(29)
},{}],4173:[function(require,module,exports){
module.exports=require(14)
},{}],4174:[function(require,module,exports){
module.exports=require(15)
},{}],4175:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4173,"./lib/window.console.js":4174}],4176:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4177,"./lib/timers.js":4178}],4177:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4169,"polyfill/polyfill-base.js":4181}],4178:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4181}],4179:[function(require,module,exports){
module.exports=require(14)
},{}],4180:[function(require,module,exports){
module.exports=require(15)
},{}],4181:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4179,"./lib/window.console.js":4180}],4182:[function(require,module,exports){
module.exports=require(14)
},{}],4183:[function(require,module,exports){
module.exports=require(15)
},{}],4184:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4182,"./lib/window.console.js":4183}],4185:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4188}],4186:[function(require,module,exports){
module.exports=require(14)
},{}],4187:[function(require,module,exports){
module.exports=require(15)
},{}],4188:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4186,"./lib/window.console.js":4187}],4189:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],4190:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":4194,"js-ext/extra/hashmap.js":4191,"polyfill/polyfill-base.js":4200}],4191:[function(require,module,exports){
module.exports=require(4)
},{}],4192:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":4193,"../lib/object.js":4194,"./classes.js":4190,"js-ext/extra/hashmap.js":4191,"polyfill/lib/weakmap.js":4198}],4193:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":4200}],4194:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4191,"polyfill/polyfill-base.js":4200,"utils":4201}],4195:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":4200}],4196:[function(require,module,exports){
module.exports=require(29)
},{}],4197:[function(require,module,exports){
module.exports=require(14)
},{}],4198:[function(require,module,exports){
module.exports=require(57)
},{}],4199:[function(require,module,exports){
module.exports=require(15)
},{}],4200:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4197,"./lib/window.console.js":4199}],4201:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4202,"./lib/timers.js":4203}],4202:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4191,"polyfill/polyfill-base.js":4206}],4203:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4206}],4204:[function(require,module,exports){
module.exports=require(14)
},{}],4205:[function(require,module,exports){
module.exports=require(15)
},{}],4206:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4204,"./lib/window.console.js":4205}],4207:[function(require,module,exports){
module.exports=require(66)
},{}],4208:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":4207}],4209:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":4207}],4210:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":4207}],4211:[function(require,module,exports){
module.exports=require(14)
},{}],4212:[function(require,module,exports){
module.exports=require(15)
},{}],4213:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4211,"./lib/window.console.js":4212}],4214:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4215,"./lib/timers.js":4216}],4215:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4191,"polyfill/polyfill-base.js":4219}],4216:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4219}],4217:[function(require,module,exports){
module.exports=require(14)
},{}],4218:[function(require,module,exports){
module.exports=require(15)
},{}],4219:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4217,"./lib/window.console.js":4218}],4220:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":4221}],4221:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":4222,"js-ext/lib/object.js":4223}],4222:[function(require,module,exports){
module.exports=require(4)
},{}],4223:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4222,"polyfill/polyfill-base.js":4226,"utils":4227}],4224:[function(require,module,exports){
module.exports=require(14)
},{}],4225:[function(require,module,exports){
module.exports=require(15)
},{}],4226:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4224,"./lib/window.console.js":4225}],4227:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4228,"./lib/timers.js":4229}],4228:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4222,"polyfill/polyfill-base.js":4232}],4229:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4232}],4230:[function(require,module,exports){
module.exports=require(14)
},{}],4231:[function(require,module,exports){
module.exports=require(15)
},{}],4232:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4230,"./lib/window.console.js":4231}],4233:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":4191,"js-ext/lib/object.js":4194,"js-ext/lib/string.js":4196,"polyfill":4213,"polyfill/extra/transition.js":4208,"polyfill/extra/vendorCSS.js":4210}],4234:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":4191,"js-ext/lib/object.js":4194,"polyfill":4213}],4235:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":4191,"js-ext/lib/object.js":4194,"js-ext/lib/string.js":4196,"polyfill":4213}],4236:[function(require,module,exports){
arguments[4][95][0].apply(exports,arguments)
},{"../css/element.css":4189,"./attribute-extractor.js":4233,"./element-array.js":4234,"./html-parser.js":4237,"./node-parser.js":4238,"./vdom-ns.js":4239,"./vnode.js":4240,"js-ext/extra/hashmap.js":4191,"js-ext/lib/object.js":4194,"js-ext/lib/promise.js":4195,"js-ext/lib/string.js":4196,"polyfill":4213,"polyfill/extra/transition.js":4208,"polyfill/extra/transitionend.js":4209,"polyfill/extra/vendorCSS.js":4210,"utils":4214,"window-ext":4220}],4237:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":4233,"./vdom-ns.js":4239,"js-ext/extra/hashmap.js":4191,"js-ext/lib/object.js":4194,"polyfill":4213}],4238:[function(require,module,exports){
arguments[4][97][0].apply(exports,arguments)
},{"./attribute-extractor.js":4233,"./vdom-ns.js":4239,"./vnode.js":4240,"js-ext/extra/hashmap.js":4191,"js-ext/lib/object.js":4194,"polyfill":4213}],4239:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":4191,"js-ext/lib/object.js":4194,"polyfill":4213}],4240:[function(require,module,exports){
"use strict";
/**
* Delivers the `vnode` prototype object, which is a virtualisation of an `Element` inside the Dom.
* These Elements work smoothless with the vdom (see ...).
*
* vnodes are much quicker to access and walk through than native dom-nodes. However, this is a module you don't need
* by itself: `Element`-types use these features under the hood.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* <br>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
*
* @module vdom
* @submodule vnode
* @class vnode
* @since 0.0.1
*/
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('js-ext/lib/string.js');
require('polyfill');
var createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.VNode) {
return window._ITSAmodules.VNode; // VNODE was already created
}
console.warn('VDOM 8');
var NS = require('./vdom-ns.js')(window),
extractor = require('./attribute-extractor.js')(window),
DOCUMENT = window.document,
LightMap = require('js-ext/extra/lightmap.js'),
MUTATION_EVENTS = new LightMap(),
BATCH_WILL_RUN = false,
xmlNS = NS.xmlNS,
nodeids = NS.nodeids,
htmlToVNodes = require('./html-parser.js')(window),
timers = require('utils/lib/timers.js'),
async = timers.async,
later = timers.later,
/*jshint proto:true */
PROTO_SUPPORTED = !!Object.__proto__,
/*jshint proto:false */
// cleanup memory after 1 minute: removed nodes SHOULD NOT be accessed afterwards
// because vnode would be recalculated and might be different from before
DESTROY_DELAY = 60000,
unescapeEntities = NS.UnescapeEntities,
NTH_CHILD_REGEXP = /^(?:(\d*)[n|N])([\+|\-](\d+))?$/, // an+b
STRING = 'string',
CLASS = 'class',
STYLE = 'style',
ID = 'id',
NODE= 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
EV_REMOVED = NODE+REMOVE,
EV_INSERTED = NODE+INSERT,
EV_CONTENT_CHANGE = NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = ATTRIBUTE+INSERT,
SPLIT_CHARACTER = createHashMap({
' ': true,
'>': true,
'+': true, // only select the element when it is immediately preceded by the former element
'~': true // only the element when it has the former element as a sibling. (just like `+`, but less strict)
}),
STORABLE_SPLIT_CHARACTER = createHashMap({
'>': true,
'+': true,
'~': true
}),
SIBLING_MATCH_CHARACTER = createHashMap({
'+': true,
'~': true
}),
ATTR_DETAIL_SPECIFIERS = createHashMap({
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to attribute-name end-tokens.
*
* @property END_ATTRIBUTENAME
* @default {
* '=': true,
* ']': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
END_ATTRIBUTENAME = createHashMap({
'=': true,
']': true,
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to different changes of Element nodeType changes.
*
* @property NODESWITCH
* @default {
* 1: {
* 1: 1,
* 3: 2,
* 8: 3
* },
* 3: {
* 1: 4,
* 3: 5,
* 8: 6
* },
* 8: {
* 1: 7,
* 3: 8,
* 8: 9
* }
* }
* @type Object
* @protected
* @since 0.0.1
*/
NODESWITCH = createHashMap({
1: createHashMap({
1: 1, // oldNodeType==Element, newNodeType==Element
3: 2, // oldNodeType==Element, newNodeType==TextNode
8: 3 // oldNodeType==Element, newNodeType==Comment
}),
3: createHashMap({
1: 4, // oldNodeType==TextNode, newNodeType==Element
3: 5, // oldNodeType==TextNode, newNodeType==TextNode
8: 6 // oldNodeType==TextNode, newNodeType==Comment
}),
8: createHashMap({
1: 7, // oldNodeType==Comment, newNodeType==Element
3: 8, // oldNodeType==Comment, newNodeType==TextNode
8: 9 // oldNodeType==Comment, newNodeType==Comment
})
}),
/**
* Object to gain quick access to selector start-tokens.
*
* @property SELECTOR_IDENTIFIERS
* @default {
* '#': 1,
* '.': 2,
* '[': 3
* }
* @type Object
* @protected
* @since 0.0.1
*/
SELECTOR_IDENTIFIERS = createHashMap({
'#': 1,
'.': 2,
'[': 3,
':': 4
}),
PSEUDO_FIRST_CHILD = ':first-child',
PSEUDO_FIRST_OF_TYPE = ':first-of-type',
PSEUDO_LAST_CHILD = ':last-child',
PSEUDO_LAST_OF_TYPE = ':last-of-type',
PSEUDO_NTH_CHILD = ':nth-child',
PSEUDO_NTH_LAST_CHILD = ':nth-last-child',
PSEUDO_NTH_LAST_OF_TYPE = ':nth-last-of-type',
PSEUDO_NTH_OF_TYPE = ':nth-of-type',
PSEUDO_ONLY_OF_TYPE = ':only-of-type',
PSEUDO_ONLY_CHILD = ':only-child',
/**
* Object to gain quick access to the selectors that required children
*
* @property PSEUDO_REQUIRED_CHILDREN
* @default {
* ':first-child': true,
* ':first-of-type': true,
* ':last-child': true,
* ':last-of-type': true,
* ':nth-child': true,
* ':nth-last-child': true,
* ':nth-last-of-type': true,
* ':nth-of-type': true,
* ':only-of-type': true,
* ':only-child': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
PSEUDO_REQUIRED_CHILDREN = createHashMap(),
_matchesSelectorItem, _matchesOneSelector, _findElementSibling, vNodeProto, _markRemoved, _tryReplaceChild,
_splitSelector, _findNodeSibling, _matchNthChild, _batchEmit, _emitDestroyChildren, _tryRemoveDomNode;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_CHILD] = true;
/**
* Searches for the next -or previous- node-sibling (nodeType of 1, 3 or 8).
*
* @method _findNodeSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findNodeSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
index = vParent.vChildNodes.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildNodes[index];
};
/**
* Searches for the next -or previous- Element-sibling (nodeType of 1).
*
* @method _findElementSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findElementSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
if (vnode.nodeType===1) {
index = vParent.vChildren.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildren[index];
}
else {
/*jshint noempty:true */
while ((vnode=_findNodeSibling(vnode, next)) && (vnode.nodeType!==1)) {}
/*jshint noempty:false */
return vnode;
}
};
/**
* Check whether the vnode matches a "nth-child" test, which is used for css pseudoselectors like `nth-child`, `nth-of-type` etc.
*
* @method _matchNthChild
* @param pseudoArg {String} the argument for nth-child
* @param index {Number} the index of the inspected vnode
* @return {Boolean} whether the vnode matches the nthChild test
* @protected
* @private
* @since 0.0.1
*/
_matchNthChild = function(pseudoArg, index) {
var match, k, a, b, nodeOk, nthIndex, sign, isNumber;
(pseudoArg==='even') && (pseudoArg='2n');
(pseudoArg==='odd') && (pseudoArg='2n+1');
match = pseudoArg.match(NTH_CHILD_REGEXP) || (isNumber=pseudoArg.validateNumber());
if (!match) {
return false;
}
// pseudoArg follows the pattern: `an+b`
if (!isNumber) {
a = match[1];
sign = match[2];
b = match[3] || 0;
(b==='') && (b=0);
}
else {
b = pseudoArg;
}
sign && (sign=sign[0]);
if (!a) {
// only fixed index to match
return (sign==='-') ? false : (parseInt(b, 10)===index);
}
else {
// we need to iterate
nodeOk = false;
b = window.Number(b);
for (k=0; !nodeOk; k++) {
nthIndex = (sign==='-') ? (a*k) - b : (a*k) + b;
if (nthIndex===index) {
nodeOk = true;
}
else if (nthIndex>index) {
// beyond index --> will never become a fix anymore
return false;
}
}
return nodeOk;
}
};
/**
* Check whether the vnode matches the css-selector. the css-selector should be a single selector,
* not multiple, so it shouldn't contain a `comma`.
*
* @method _matchesOneSelector
* @param vnode {vnode} the vnode to inspect
* @param selector {String} the selector-item to check the match for
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches the css-selector
* @protected
* @private
* @since 0.0.1
*/
_matchesOneSelector = function(vnode, selector, relatedVNode) {
var selList = _splitSelector(selector),
size = selList.length,
selMatch = false,
i, selectorItem, last,
rightvnode, relationMatch, checkRelation;
if (STORABLE_SPLIT_CHARACTER[selList[size-1]]) {
return false;
}
relationMatch = function(leftVNode, rightVNode, relationCharacter) {
var match, vParent, vChildren;
// when `selector` starts with `>`, `~` or `+`, then
// there should also be a match comparing a related node!
switch (relationCharacter) {
case '>':
leftVNode || (leftVNode=rightVNode.vParent);
match = (leftVNode.vChildren.indexOf(rightVNode)!==-1);
break;
case '~':
leftVNode || (leftVNode=rightVNode.vFirstElement);
vParent = leftVNode.vParent;
vChildren = vParent && vParent.vChildren;
match = vParent && (vChildren.indexOf(leftVNode)<vChildren.indexOf(rightVNode));
break;
case '+':
leftVNode || (leftVNode=rightVNode.vPreviousElement);
match = (leftVNode.vNextElement === rightVNode);
}
return !!match;
};
last = size - 1;
i = last;
while (vnode && ((selMatch || (i===last)) && (i>=0))) {
selectorItem = selList[i];
if (STORABLE_SPLIT_CHARACTER[selectorItem]) {
checkRelation = selectorItem;
i--;
}
else {
selMatch = _matchesSelectorItem(vnode, selectorItem);
if (!selMatch && (i===last)) {
return false;
}
while (!selMatch && vnode && (i!==last)) {
// may also look at nodes higher in the chain
vnode = SIBLING_MATCH_CHARACTER[selList[i+1]] ? vnode.vPreviousElement : vnode.vParent;
selMatch = vnode && _matchesSelectorItem(vnode, selectorItem);
}
selMatch && checkRelation && vnode && (selMatch=relationMatch(vnode, rightvnode, checkRelation));
rightvnode = vnode;
if (vnode) {
// do a precheck on the next checkRelation:
// and determine whether to set the vnode upper the tree or at the previous sibling:
vnode = SIBLING_MATCH_CHARACTER[selList[i-1]] ? vnode.vPreviousElement : vnode.vParent;
}
if (selMatch) {
checkRelation = false;
i--;
}
}
}
if ((rightvnode===relatedVNode) || (i!==-1)) {
selMatch = false;
}
selMatch && checkRelation && rightvnode && (selMatch=relationMatch(relatedVNode, rightvnode, checkRelation));
return selMatch;
};
/**
* Check whether the vnode matches one specific selector-item. Suppose the css-selector: "#mynode li.red .blue"
* then there are 3 selector-items: "#mynode", "li.red" and ".blue"
*
* This method also can handle the new selectors:
* <ul>
* <li>[att^=val] –-> the “begins with” selector</li>
* <li>[att$=val] –-> the “ends with” selector</li>
* <li>[att*=val] –-> the “contains” selector (might be a substring)</li>
* <li>[att~=val] –-> the “contains” selector as a separate word, separated by spaces</li>
* <li>[att|=val] –-> the “contains” selector as a separate word, separated by `|`</li>
* <li>+ --> (same level)</li>
* <li>~ --> (same level)</li>
* </ul>
*
* @method _matchesSelectorItem
* @param vnode {Object} the vnode to inspect
* @param selectorItem {String} the selector-item to check the match for
* @return {Boolean} whether the vnode matches the selector-item
* @protected
* @private
* @since 0.0.1
*/
_matchesSelectorItem = function (vnode, selectorItem) {
var i = 0,
len = selectorItem.length,
character = selectorItem[0],
tagName, id, className, attributeName, attributeValue, stringMarker, attributeisString, isBoolean, insideAttributeValue, insideAttribute,
vParent, checkBoolean, treatment, k, min, max, value, len2, index, found, pseudo, pseudoArg, arglevel, count, vParentVChildren;
if (selectorItem==='*') {
return true;
}
if (!SELECTOR_IDENTIFIERS[character]) {
// starts with tagName
tagName = '';
// reposition i to continue in the right way:
i--;
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
tagName += character;
}
if (tagName.toUpperCase()!==vnode.tag) {
return false;
}
}
while (i<len) {
switch (character) {
case '#':
id = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
id += character;
}
if (id!==vnode.id) {
return false;
}
break;
case '.':
className = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
className += character;
}
if (!vnode.hasClass(className)) {
return false;
}
break;
case '[':
attributeName = '';
while ((++i<len) && (character=selectorItem[i]) && !END_ATTRIBUTENAME[character]) {
attributeName += character;
}
// if character===']' then we have an attribute without a value-definition
if (!vnode.attrs[attributeName] || ((character===']') && (vnode.attrs[attributeName]!==''))) {
return !!vnode.attrs[attributeName];
}
// now we read the value of the attribute
// however, it could be that the selector has a special `detailed` identifier set (defined by: ATTR_DETAIL_SPECIFIERS)
if (ATTR_DETAIL_SPECIFIERS[character]) {
treatment = character; // store the character to know how the attributedata should be treaded
i++; // character should be a "=" by now
}
else {
treatment = null;
}
attributeValue = '';
stringMarker = selectorItem[i+1];
attributeisString = (stringMarker==='"') || (stringMarker==="'");
attributeisString && (i++);
// end of attributaValue = (character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker))
while ((++i<len) && (character=selectorItem[i]) && !((character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker)))) {
attributeValue += character;
}
if (attributeisString) {
// if attribute is string, then we need to _remove to last stringmarker
attributeValue = attributeValue.substr(0, attributeValue.length-1);
}
else {
// if attribute is no string, then we need to typecast its value
isBoolean = ((attributeValue.length>3) && (attributeValue.length<6) &&
(checkBoolean=attributeValue.toUpperCase()) &&
((checkBoolean==='FALSE') || (checkBoolean==='TRUE')));
// typecast the value to either Boolean or Number:
attributeValue = isBoolean ? (checkBoolean==='TRUE') : parseFloat(attributeValue);
}
// depending upon how the attributedata should be treated:
if (treatment) {
switch (treatment) {
case '^': // “begins with” selector
if (!vnode.attrs[attributeName].startsWith(attributeValue)) {
return false;
}
break;
case '$': // “ends with” selector
if (!vnode.attrs[attributeName].endsWith(attributeValue)) {
return false;
}
break;
case '*': // “contains” selector (might be a substring)
if (!vnode.attrs[attributeName].contains(attributeValue)) {
return false;
}
break;
case '~': // “contains” selector as a separate word, separated by spaces
if (!(' '+vnode.attrs[attributeName]+' ').contains(' '+attributeValue+' ')) {
return false;
}
break;
case '|': // “contains” selector as a separate word, separated by `|`
if (!('|'+vnode.attrs[attributeName]+'|').contains('|'+attributeValue+'|')) {
return false;
}
break;
}
}
else if (vnode.attrs[attributeName]!==attributeValue) {
return false;
}
// we still need to increase one position:
(++i<len) && (character=selectorItem[i]);
break;
case ':':
// we have a pseudo-selector
// first, find out which one
// because '::' is a valid start (though without any selection), we start to back the next character as well:
pseudo = ':'+selectorItem[++i];
pseudoArg = '';
vParent = vnode.vParent;
vParentVChildren = vParent && vParent.vChildren;
// pseudo-selectors might have an argument passed in, like `:nth-child(2n+1)` or `:not([type="checkbox"])` --> we
// store this argument inside `pseudoArg`
// also note that combinations are possible with `:not` --> `:not(:nth-child(2n+1))`
// also note that we cannot "just" look for a closing character when running into the usage of attributes:
// for example --> `:not([data-x="some data :)"])`
// that's why -once we are inside attribute-data- we need to continue until the attribute-data ends
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
if (character==='(') {
// starting arguments
arglevel = 1;
insideAttribute = false;
insideAttributeValue = false;
while ((++i<len) && (character=selectorItem[i]) && (arglevel>0)) {
if (!insideAttribute) {
if (character==='(') {
arglevel++;
}
else if (character===')') {
arglevel--;
}
else if (character==='[') {
insideAttribute = true;
}
}
else {
// inside attribute
if (!insideAttributeValue) {
if ((character==='"') || (character==="'")) {
insideAttributeValue = true;
stringMarker = character;
}
else if (character===']') {
insideAttribute = false;
}
}
else if ((character===stringMarker) && (selectorItem[i+1]===']')) {
insideAttributeValue = false;
}
}
(arglevel>0) && (pseudoArg+=character);
}
}
else {
pseudo += character;
}
}
// now, `pseudo` is known as well as its possible pseudoArg
if (!vParentVChildren && PSEUDO_REQUIRED_CHILDREN[pseudo]) {
return false;
}
switch (pseudo) {
case ':checked': // input:checked Selects every checked <input> element
if (!vnode.attrs.checked) {
return false;
}
break;
case ':disabled': // input:disabled Selects every disabled <input> element
if (!vnode.attrs.disabled) {
return false;
}
break;
case ':empty': // p:empty Selects every <p> element that has no children (including text nodes)
if (vnode.vChildNodes && (vnode.vChildNodes.length>0)) {
return false;
}
break;
case ':enabled': // input:enabled Selects every enabled <input> element
if (vnode.attrs.disabled) {
return false;
}
break;
case PSEUDO_FIRST_CHILD: // p:first-child Selects every <p> element that is the first child of its parent
if (vParentVChildren[0]!==vnode) {
return false;
}
break;
case PSEUDO_FIRST_OF_TYPE: // p:first-of-type Selects every <p> element that is the first <p> element of its parent
for (k=vParentVChildren.indexOf(vnode)-1; k>=0; k--) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':focus': // input:focus Selects the input element which has focus
if (vnode.domNode!==DOCUMENT.activeElement) {
return false;
}
break;
case ':in-range': // input:in-range Selects input elements with a value within a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || (value<min) || (value>max)) {
return false;
}
break;
case ':lang': // p:lang(it) Selects every <p> element with a lang attribute equal to "it" (Italian)
if (vnode.attrs.lang!==pseudoArg) {
return false;
}
break;
case PSEUDO_LAST_CHILD: // p:last-child Selects every <p> element that is the last child of its parent
if (vParentVChildren[vParentVChildren.length-1]!==vnode) {
return false;
}
break;
case PSEUDO_LAST_OF_TYPE: // p:last-of-type Selects every <p> element that is the last <p> element of its parent
len2 = vParentVChildren.length;
for (k=vParentVChildren.indexOf(vnode)+1; k<len2; k++) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':not': // :not(p) Selects every element that is not a <p> element
if (vnode.matchesSelector(pseudoArg)) {
return false;
}
break;
case PSEUDO_NTH_CHILD: // p:nth-child(2) Selects every <p> element that is the second child of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
index = vParentVChildren.indexOf(vnode)+1;
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_CHILD: // p:nth-last-child(2) Selects every <p> element that is the second child of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_OF_TYPE: // p:nth-last-of-type(2) Selects every <p> element that is the second <p> element of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
index = 0;
for (k=vParentVChildren.length-1; (k>=0) && !found; k--) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_OF_TYPE: // p:nth-of-type(2) Selects every <p> element that is the second <p> element of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
len2 = vParentVChildren.length;
index = 0;
for (k=0; (k<len2) && !found; k++) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_ONLY_OF_TYPE: // p:only-of-type Selects every <p> element that is the only <p> element of its parent
len2 = vParentVChildren.length;
count = 0;
for (k=0; (k<len2) && (count<=1); k++) {
(vParentVChildren[k].tag===vnode.tag) && count++;
}
if (count!==1) {
return false;
}
break;
case PSEUDO_ONLY_CHILD: // p:only-child Selects every <p> element that is the only child of its parent
if (vParentVChildren.length!==1) {
return false;
}
break;
case ':optional': // input:optional Selects input elements with no "required" attribute
if (vnode.attrs.required) {
return false;
}
break;
case ':out-of-range': // input:out-of-range Selects input elements with a value outside a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || ((value>=min) && (value<=max))) {
return false;
}
break;
case ':read-only': // input:read-only Selects input elements with the "readonly" attribute specified
if (!vnode.attrs.readonly) {
return false;
}
break;
case ':read-write': // input:read-write Selects input elements with the "readonly" attribute NOT specified
if (vnode.attrs.readonly) {
return false;
}
break;
case ':required': // input:required Selects input elements with the "required" attribute specified
if (!vnode.attrs.required) {
return false;
}
break;
case ':root': // Selects the document's root element
if (vnode.domNode!==DOCUMENT.documentElement) {
return false;
}
break;
}
}
}
return true;
};
/**
* Splits the selector into separate subselector-items that should match different elements through the tree.
* Special characters '>' and '+' are added as separate items in the hash.
*
* @method _splitSelector
* @param selector {String} the selector-item to check the match for
* @return {Array} splitted selectors
* @protected
* @private
* @since 0.0.1
*/
_splitSelector = function(selector) {
var list = [],
len = selector.length,
sel = '',
i, character, insideDataAttr;
for (i=0; i<len; i++) {
character = selector[i];
if (character==='[') {
sel += character;
insideDataAttr = true;
}
else if (character===']') {
sel += character;
insideDataAttr = false;
}
else if (insideDataAttr || !SPLIT_CHARACTER[character]) {
sel += character;
}
else {
// unique selectoritem is found, add it to the list
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
// in case the last character was '>', '+' or '~', we need to add it as a separate item
STORABLE_SPLIT_CHARACTER[character] && (list[list.length]=character);
}
}
// add the last item
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
return list;
};
_batchEmit = function() {
// we will exactly define the 'UI:'-event --> Itags will have another emitterName
MUTATION_EVENTS.each(function (mutationEvents, vnode) {
var domNode = vnode.domNode;
if (mutationEvents[EV_REMOVED]) {
domNode.emit('UI:'+EV_REMOVED);
}
else if (mutationEvents[EV_INSERTED]) {
domNode.emit('UI:'+EV_INSERTED);
}
else {
// contentchange and attributechanges can go hand in hand
mutationEvents.each(function(value, evt) {
domNode.emit('UI:'+evt, (evt===EV_CONTENT_CHANGE) ? null : {changed: value});
});
}
});
MUTATION_EVENTS.clear();
BATCH_WILL_RUN = false;
};
_emitDestroyChildren = function(vnode) {
var children = vnode.vChildren,
len = children.length,
i, vChild;
for (i=0; i<len; i++) {
vChild = children[i];
vChild._emit(EV_REMOVED);
}
};
_markRemoved = function(vnode) {
var vChildNodes = vnode.vChildNodes,
len, i, vChildNode;
if (vnode.nodeType===1) {
Object.protectedProp(vnode, 'removedFromDOM', true);
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && _markRemoved(vChildNode);
}
}
}
};
/**
* A safe way to remove a dom-node, even if it is not present.
* Will not throw a JS error when the "to be removed" node isn't in the dom
*
* @method _tryRemoveDomNode
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param childDomNode {DOMNode} the node that needs to be removed
* @since 0.0.1
*/
_tryRemoveDomNode = function(parentDomNode, childDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && childDomNode.isItag && childDomNode.isItag()) {
DOCUMENT._itagList.remove(childDomNode);
}
try {
parentDomNode._removeChild(childDomNode);
}
catch(err) {}
};
/**
* A safe way to replace a dom-node, even if the "to be replaced"-node it is not present.
* Will not throw a JS error when the "to be replaced" node isn't in the dom. in that case, the new node will
* be appended
*
* @method _tryReplaceChild
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param newChildDomNode {DOMNode} the node that needs to be replaced
* @param oldChildDomNode {DOMNode} the node that needs to be inserted
* @since 0.0.1
*/
_tryReplaceChild = function(parentDomNode, newChildDomNode, oldChildDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && oldChildDomNode.isItag && oldChildDomNode.isItag()) {
DOCUMENT._itagList.remove(oldChildDomNode);
}
try {
parentDomNode._replaceChild(newChildDomNode, oldChildDomNode);
}
catch(err) {
// if the childDomNode isn;t there any more - for whatever reason - the we need to append the newChildNode
parentDomNode._appendChild(newChildDomNode);
}
};
vNodeProto = window._ITSAmodules.VNode = {
/**
* Check whether the vnode's domNode is equal, or contains the specified Element.
*
* @method contains
* @return {Boolean} whether the vnode's domNode is equal, or contains the specified Element.
* @since 0.0.1
*/
contains: function(otherVNode, noProtectedSearch) {
var instance = this;
if (otherVNode && (otherVNode.destroyed || (noProtectedSearch && otherVNode._systemNode))) {
return false;
}
while (otherVNode && (otherVNode!==instance)) {
otherVNode = otherVNode.vParent;
if (otherVNode && noProtectedSearch && (otherVNode._systemNode || (otherVNode.isItag && otherVNode.domNode.contentHidden))) {
return false;
}
}
return (otherVNode===instance);
},
/**
* Empties the vnode.
*
* Syncs with the dom.
*
* @method empty
* @param [full=false] {Boolean} whether system-nodes should be removed as well
* @chainable
* @since 0.0.1
*/
empty: function(full) {
delete this._scripts;
return this._setChildNodes([], null, full);
},
/**
* Returns the first child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method firstOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the first child-vnode or null when not present
* @since 0.0.1
*/
firstOfVChildren: function(cssSelector) {
var instance = this,
found, i, len, vChildren, element;
if (!cssSelector) {
return instance.vFirstElementChild;
}
vChildren = instance.vChildren;
len = vChildren.length;
for (i=0; !found && (i<len); i++) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
return found;
},
/**
* Gets the innerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* Only valid for nodetype=1 (HTMLElements)
*
* @method getHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String|undefined} the innerHTML without the elements specified, or `undefined` when not an HTMLElement
* @since 0.0.1
*/
getHTML: function(exclude, includeSystemNodes) {
var instance = this,
html, vChildNodes, len, i, vChildNode;
if (instance.nodeType===1) {
if (exclude) {
Array.isArray(exclude) || (exclude=[exclude]);
}
html = '';
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
switch (vChildNode.nodeType) {
case 1:
(exclude && exclude.contains(vChildNode.domNode)) || (!includeSystemNodes && vChildNode._systemNode) || (html+=vChildNode.getOuterHTML(exclude, includeSystemNodes));
break;
case 3:
html += vChildNode.text.replace(/</g, '<').replace(/>/g, '>');
break;
case 8:
html += '<!--' + vChildNode.text.replace(/</g, '<').replace(/>/g, '>') + '-->';
}
}
}
return html;
},
/**
* Gets the outerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* @method getOuterHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String} the outerHTML
* @since 0.0.1
*/
getOuterHTML: function(exclude, includeSystemNodes) {
var instance = this,
html,
attrs = instance.attrs;
if (instance.nodeType===1) {
if (instance.nodeType!==1) {
return instance.textContent;
}
html = '<' + instance.tag.toLowerCase();
attrs.each(function(value, key) {
html += ' '+key+'="'+value+'"';
});
instance.isVoid && (html += '/');
html += '>';
if (!instance.isVoid) {
html += ((!includeSystemNodes && instance.isItag) ? '' : instance.getHTML(exclude, includeSystemNodes)) + '</' + instance.tag.toLowerCase() + '>';
}
}
return html;
},
/**
* Checks whether the vnode has any vChildNodes (nodeType of 1, 3 or 8).
*
* @method hasVChildNodes
* @return {Boolean} whether the vnode has any vChildNodes.
* @since 0.0.1
*/
hasVChildNodes: function() {
return this.vChildNodes ? (this.vChildNodes.length>0) : false;
},
/**
* Checks whether the vnode has any vChildren (vChildNodes with nodeType of 1).
*
* @method hasVChildren
* @return {Boolean} whether the vnode has any vChildren.
* @since 0.0.1
*/
hasVChildren: function() {
return this.vChildNodes ? (this.vChildren.length>0) : false;
},
/**
* Checks whether the className is present on the vnode.
*
* @method hasClass
* @param className {String|Array} the className to check for. May be an Array of classNames, which all needs to be present.
* @return {Boolean} whether the className (or classNames) is present on the vnode
* @since 0.0.1
*/
hasClass: function(className) {
var instance = this,
check = function(cl) {
return !!instance.classNames[cl];
};
if (!instance.classNames) {
return false;
}
if (typeof className === STRING) {
return check(className);
}
else if (Array.isArray(className)) {
return className.every(check);
}
return false;
},
/**
* Returns the last child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method lastOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the last child-vnode or null when not present
* @since 0.0.1
*/
lastOfVChildren: function(cssSelector) {
var vChildren = this.vChildren,
found, i, element;
if (vChildren) {
if (!cssSelector) {
return this.vLastElementChild;
}
for (i=vChildren.length-1; !found && (i>=0); i--) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
}
return found;
},
/**
* Checks whether the vnode matches one of the specified selectors. `selectors` can be one, or multiple css-selectors,
* separated by a `comma`. For example: "#myid li.red blue" is one selector, "div.red, div.blue, div.green" are three selectors.
*
* @method matchesSelector
* @param selectors {String} one or more css-selectors
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches one of the selectors
* @since 0.0.1
*/
matchesSelector: function(selectors, relatedVNode) {
var instance = this;
if (instance.nodeType!==1) {
return false;
}
selectors = selectors.split(',');
// we can use Array.some, because there won't be many separated selectoritems,
// so the final invocation won't be delayed much compared to looping
return selectors.some(function(selector) {
return _matchesOneSelector(instance, selector, relatedVNode);
});
},
/**
* Reloads the DOM-attribute into the vnode.
*
* @method matchesSelector
* @param attributeName {String} the name of the attribute to be reloaded.
* @return {Node} the domNode that was reloaded.
* @since 0.0.1
*/
reloadAttr: function(attributeName) {
var instance = this,
domNode = instance.domNode,
attributeValue = domNode._getAttribute(attributeName),
attrs = instance.attrs,
extractStyle, extractClass;
if (instance.nodeType===1) {
attributeValue || (attributeValue='');
if (attributeValue==='') {
delete attrs[attributeName];
// in case of STYLE attributeName --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attributeName --> special treatment
(attributeName===CLASS) && (instance.classNames={});
// in case of ID attributeName --> special treatment
if ((attributeName===ID) && (instance.id)) {
delete nodeids[instance.id];
delete instance.id;
}
}
else {
attrs[attributeName] = attributeValue;
// in case of STYLE attributeName --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(attributeValue);
attributeValue = extractStyle.attrStyle;
if (attributeValue) {
attrs.style = attributeValue;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attributeName --> special treatment
extractClass = extractor.extractClass(attributeValue);
attributeValue = extractClass.attrClass;
if (attributeValue) {
attrs[CLASS] = attributeValue;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (instance.id!==attributeValue) && (delete nodeids[instance.id]);
instance.id = attributeValue;
nodeids[attributeValue] = domNode;
}
}
}
return domNode;
},
/**
* Returns the vnode's style in a serialized form: the way it appears in the dom.
*
* @method serializeStyles
* @return {String} vnode's style
* @since 0.0.1
*/
serializeStyles: function() {
return extractor.serializeStyles(this.styles);
},
/**
* Sets the vnode's and dom-nodes inner HTML.
*
* Syncs with the dom. Can be invoked multiple times without issues.
*
* @method setHTML
* @param content {String} the innerHTML
* @param [suppressItagRender] {Boolean} to suppress Itags from rendering
* @param [allowScripts=false] {Boolean} whether scripts are allowed --> these should be defined with `xscript` instead of `script`
* @chainable
* @since 0.0.1
*/
setHTML: function(content, suppressItagRender, allowScripts) {
var instance = this;
instance._setChildNodes(htmlToVNodes(content, vNodeProto, instance.ns, null, suppressItagRender, allowScripts), suppressItagRender);
return instance;
},
/**
* Syncs the vnode's nodeid (if available) inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom. Can be invoked multiple times without issues.
*
* @method storeId
* @chainable
* @since 0.0.1
*/
storeId: function() {
// store node/vnode inside WeakMap:
var instance = this;
instance.id ? (nodeids[instance.id]=instance.domNode) : (delete nodeids[instance.id]);
return instance;
},
//---- private ------------------------------------------------------------------
_addToTaglist: function() {
var instance = this,
itagList;
if (instance.isItag) {
itagList = DOCUMENT.getItags(); // also reads the dom if the list isn't build yet
instance._data || Object.protectedProp(instance, '_data', {});
if (!instance._data.ce_destroyed && !itagList.contains(instance.domNode)) {
itagList.push(instance.domNode);
}
}
},
/**
* Adds a vnode to the end of the list of vChildNodes.
*
* Syncs with the DOM.
*
* @method _appendChild
* @param VNode {vnode} vnode to append
* @private
* @return {Node} the Node that was appended
* @since 0.0.1
*/
_appendChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
size, noProccessScript, scriptContent;
if ((VNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (VNode.attrs && VNode.attrs.src) ? VNode.attrs.src : VNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
VNode._moveToParent(instance);
instance.domNode._appendChild(domNode);
if (VNode.nodeType===3) {
size = instance.vChildNodes.length;
instance._normalize();
// if the size changed, then the domNode was merged
(size===instance.vChildNodes.length) || (domNode=instance.vChildNodes[instance.vChildNodes.length-1].domNode);
}
if (VNode.nodeType===1) {
VNode._addToTaglist();
VNode._emit(EV_INSERTED);
}
return domNode;
}
},
/**
* Removes the vnode from its parent vChildNodes- and vChildren-list.
*
* Does NOT sync with the dom.
*
* @method _deleteFromParent
* @private
* @chainable
* @since 0.0.1
*/
_deleteFromParent: function() {
var instance = this,
vParent = instance.vParent;
if (vParent && vParent.vChildNodes) {
vParent.vChildNodes.remove(instance);
// force to recalculate the vChildren on a next call:
(instance.nodeType===1) && (vParent._vChildren=null);
}
return instance;
},
/**
* Cleans up (empties) vnode's `_data`
*
* @method _cleanData
* @private
* @chainable
* @since 0.0.1
*/
_cleanData: function() {
var instance = this,
data = instance._data;
data && data.each(
function(value, key) {
delete data[key];
}
);
return instance;
},
/**
* Cleans up (removes) duplicated `style` definitions.
*
* @method _cleanupStyle
* @private
* @chainable
* @since 0.0.1
*/
_cleanupStyle: function() {
var instance = this,
compare = [],
removal = [],
vChildren = instance.vChildren,
len = vChildren.length,
vChild, i, styleContent, vChildInner;
for (i=0; i<len; i++) {
vChild = vChildren[i];
if (vChild.tag==='STYLE') {
vChildInner = vChild.vChildNodes[0];
if (vChildInner) {
styleContent = vChildInner.text;
if (compare.contains(styleContent)) {
removal[removal.length] = vChild;
}
else {
compare[compare.length] = styleContent;
}
}
else {
// empty style-tag
removal[removal.length] = vChild;
}
}
}
removal.forEach(function(vnode) {
instance._removeChild(vnode);
});
},
/**
* Destroys the vnode and all its vnode-vChildNodes.
* Removes it from its vParent.vChildNodes list,
* also removes its definitions inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom.
*
* @method _destroy
* @private
* @chainable
* @since 0.0.1
*/
_destroy: function(silent) {
var instance = this,
vChildNodes = instance.vChildNodes,
len, i, vChildNode, vParent, treeNodes;
if (!instance.destroyed) {
if (!silent) {
// Because we don't want to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
later(function() {
instance._emit(EV_REMOVED, null, null, null, true);
}, 5);
}
Object.protectedProp(instance, 'destroyed', true);
// first: determine the dom-tree, which module `event-dom` needs to determine where the node was before it was destroyed:
treeNodes = [instance];
vParent = instance.vParent;
while (vParent) {
treeNodes[treeNodes.length] = vParent;
vParent = vParent.vParent;
}
// mark all its vChildNodes so we can see if the node is in the DOM
_markRemoved(instance);
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && instance.isItag) {
DOCUMENT._itagList.remove(instance.domNode);
}
instance._scripts && (instance._scripts.length=0);
// The definite cleanup needs to be done after a timeout:
// someone might need to handle the Element when removed (fe to cleanup specific things)
later(function() {
instance._cleanData();
if (instance.nodeType===1) {
// _destroy all its vChildNodes
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && vChildNode._destroy(true);
}
}
}
instance._vChildren = null;
// explicitely set instance.domNode._vnode and instance.domNode to null in order to prevent problems with the GC (we break the circular reference)
delete instance.domNode._vnode;
// if valid id, then _remove the DOMnodeRef from internal hash
instance.id && delete nodeids[instance.id];
}, silent ? 0 : DESTROY_DELAY);
instance._deleteFromParent();
// Do not make domNode `null` --> it could be used even when not in the dom
}
return instance;
},
_emit: function(evt, attribute, newValue, prevValue, destroyEvt) {
/**
* Emitted by every Element that gets inserted.
*
* @event nodeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets removed.
*
* @event noderemove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets its content changed (innerHTML/innerText).
*
* @event nodecontentchange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute inserted.
*
* @event attributeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* </ul>
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute removed.
*
* @event attributeremove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Strings of the attributeNames that are removed
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute changed.
*
* @event attributechange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* <li>prevValue</li>
* </ul>
* @since 0.1
*/
var instance = this,
silent, attrMutations, mutationEvents, mutation, vParent;
if (!DOCUMENT.hasMutationSubs || (instance.nodeType!==1)) {
return;
}
silent = !!DOCUMENT._suppressMutationEvents;
if (!silent && (destroyEvt || !instance.destroyed)) {
// Because we don't wannt to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
mutationEvents = MUTATION_EVENTS.get(instance) || {};
if (attribute) {
attrMutations = mutationEvents[evt] || [];
if (evt===EV_ATTRIBUTE_REMOVED) {
mutation = attribute;
}
else {
mutation = {
attribute: attribute
};
if ((evt===EV_ATTRIBUTE_INSERTED) || (evt===EV_ATTRIBUTE_CHANGED)) {
mutation.newValue = newValue;
}
if ((evt===EV_ATTRIBUTE_CHANGED) && prevValue) {
mutation.prevValue = prevValue;
}
}
attrMutations.push(mutation);
mutationEvents[evt] = attrMutations;
}
else {
mutationEvents[evt] = true;
}
MUTATION_EVENTS.set(instance, mutationEvents);
// now set all parent to have a nodecontentchange:
vParent = instance.vParent;
vParent && vParent._emit(EV_CONTENT_CHANGE);
// in case of removal we need to emit EV_REMOVED for all children right now
// for they will be actually removed silently after a delay of 1 minute
(evt===EV_REMOVED) && _emitDestroyChildren(instance);
if (!BATCH_WILL_RUN) {
BATCH_WILL_RUN = true;
async(function() {
_batchEmit();
});
}
}
return instance;
},
/**
* Inserts `newVNode` before `refVNode`.
*
* Syncs with the DOM.
*
* @method _insertBefore
* @param newVNode {vnode} vnode to insert
* @param refVNode {vnode} The vnode before which newVNode should be inserted.
* @private
* @return {Node} the Node being inserted (equals domNode)
* @since 0.0.1
*/
_insertBefore: function(newVNode, refVNode) {
var instance = this,
domNode = newVNode.domNode,
index = instance.vChildNodes.indexOf(refVNode),
noProccessScript, scriptContent;
if ((newVNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (newVNode.attrs && newVNode.attrs.src) ? newVNode.attrs.src : newVNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
if (index!==-1) {
newVNode._moveToParent(instance, index);
instance.domNode._insertBefore(domNode, refVNode.domNode);
(newVNode.nodeType===3) && instance._normalize();
if (newVNode.nodeType===1) {
newVNode._addToTaglist();
newVNode._emit(EV_INSERTED);
}
return domNode;
}
else {
console.warn('trying to insert before, but no ref-node found. Will append the new node');
return instance._appendChild(newVNode);
}
}
},
/**
* Moves the vnode from its current parent.vChildNodes list towards a new parent vnode at the specified position.
*
* Does NOT sync with the dom.
*
* @method _moveToParent
* @param parentVNode {vnode} the parent-vnode
* @param [index] {Number} the position of the child. When not specified, it will be appended.
* @private
* @chainable
* @since 0.0.1
*/
_moveToParent: function(parentVNode, index) {
var instance = this,
vParent = instance.vParent;
instance._deleteFromParent();
instance.vParent = parentVNode;
parentVNode.vChildNodes || (parentVNode.vChildNodes=[]);
(typeof index==='number') ? parentVNode.vChildNodes.insertAt(instance, index) : (parentVNode.vChildNodes[parentVNode.vChildNodes.length]=instance);
// force to recalculate the vChildren on a next call:
vParent && (instance.nodeType===1) && (vParent._vChildren = null);
// force to recalculate the vChildren on a next call:
parentVNode && (instance.nodeType===1) && (parentVNode._vChildren=null);
return instance;
},
/**
* Removes empty TextNodes and merges following TextNodes inside the vnode.
*
* Syncs with the dom.
*
* @method _normalize
* @private
* @chainable
* @since 0.0.1
*/
_normalize: function() {
var instance = this,
domNode = instance.domNode,
vChildNodes = instance.vChildNodes,
changed = false,
i, preChildNode, vChildNode;
if (!instance._unNormalizable && vChildNodes) {
for (i=vChildNodes.length-1; i>=0; i--) {
vChildNode = vChildNodes[i];
preChildNode = vChildNodes[i-1]; // i will get the value `-1` eventually, which leads into undefined preChildNode
if (vChildNode.nodeType===3) {
if (vChildNode.text==='') {
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
else if (preChildNode && preChildNode.nodeType===3) {
preChildNode.text += vChildNode.text;
preChildNode.domNode.nodeValue = unescapeEntities(preChildNode.text);
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
}
}
}
changed && instance._emit(EV_CONTENT_CHANGE);
return instance;
},
/**
* Makes the vnode `normalizable`. Could be set to `false` when batch-inserting nodes, while `normalizaing` manually at the end.
* Afterwards, you should always reset `normalizable` to true.
*
* @method _normalizable
* @param value {Boolean} whether the vnode should be normalisable.
* @private
* @chainable
* @since 0.0.1
*/
_normalizable: function(value) {
var instance = this;
value ? (delete instance._unNormalizable) : (instance._unNormalizable=true);
return instance;
},
/**
* Prevents MutationObserver from making the dom sync with the vnode.
* Should be used when manipulating the dom from within the vnode itself (to preventing looping)
*
* @method _noSync
* @chainable
* @private
* @since 0.0.1
*/
_noSync: function() {
var instance = this;
if (!instance._nosync) {
instance._nosync = true;
async(function() {
instance._nosync = false;
});
}
return instance;
},
/**
* Removes the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _removeAttr
* @param attributeName {String}
* @private
* @chainable
* @since 0.0.1
*/
_removeAttr: function(attributeName, suppressItagRender) {
var instance = this,
attributeNameSplitted, ns;
if (instance.isItag && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
console.warn('Not allowed to remove the attribute '+attributeName);
return instance;
}
if (instance.attrs[attributeName]!==undefined) {
delete instance.attrs[attributeName];
// in case of STYLE attribute --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attribute --> special treatment
(attributeName===CLASS) && (instance.classNames={});
if (attributeName===ID) {
delete nodeids[instance.id];
delete instance.id;
}
instance._emit(EV_ATTRIBUTE_REMOVED, attributeName);
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
instance.domNode._removeAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName);
}
else {
instance.domNode._removeAttribute(attributeName);
}
}
return instance;
},
/**
* Removes the vnode's child-vnode from its vChildren and the DOM.
*
* Syncs with the DOM.
*
* @method removeChild
* @param VNode {vnode} the child-vnode to remove
* @private
* @since 0.0.1
*/
_removeChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
hadFocus = domNode && domNode.hasFocus && domNode.hasFocus() && (VNode.attrs['fm-lastitem']==='true'),
parentVNode = VNode.vParent;
VNode._destroy();
_tryRemoveDomNode(instance.domNode, VNode.domNode);
instance._normalize();
// now, reset the focus on focusmanager when needed:
if (hadFocus) {
while (parentVNode && !parentVNode.attrs['fm-manage']) {
parentVNode = parentVNode.vParent;
}
parentVNode && parentVNode.domNode.focus();
}
},
/**
* Replaces the current vnode at the parent.vChildNode list by `newVNode`
*
* Does NOT sync with the dom.
*
* @method _replaceAtParent
* @param newVNode {Object} the new vnode which should take over the place of the current vnode
* @private
* @chainable
* @since 0.0.1
*/
_replaceAtParent: function(newVNode) {
var instance = this,
vParent = instance.vParent,
vChildNodes, index;
if (vParent && (vChildNodes=vParent.vChildNodes)) {
index = vChildNodes.indexOf(instance);
// force to recalculate the vChildren on a next call:
((instance.nodeType===1) || (newVNode.nodeType===1)) && (instance.vParent._vChildren=null);
vChildNodes[index] = newVNode;
}
return instance._destroy();
},
/**
* Sets the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _setAttr
* @param attributeName {String}
* @param value {String} the value for the attributeName
* @param [force=false] {Boolean} force the attribute to be set, even if restrictions would deny it
* @private
* @chainable
* @since 0.0.1
*/
_setAttr: function(attributeName, value, force, suppressItagRender) {
var instance = this,
extractStyle, extractClass,
attrs = instance.attrs,
prevVal = attrs[attributeName],
domNode = instance.domNode,
attributeNameSplitted, ns;
if (instance.isItag && !force && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
if (prevVal!=value) {
console.warn('Not allowed to set the attribute '+attributeName);
}
return instance;
}
// don't check by !== --> value isn't parsed into a String yet
if (prevVal && ((value===undefined) || (value===null))) {
instance._removeAttr(attributeName, suppressItagRender);
return instance;
}
// attribute-values are always Strings:
value = String(value);
// attribute-values will be stored without " or '
value = value.replace(/"/g, '"').replace(/'/g, "'");
if (prevVal!=value) {
attrs[attributeName] = value;
// in case of STYLE attribute --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(value);
value = extractStyle.attrStyle;
if (value) {
attrs.style = value;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attribute --> special treatment
extractClass = extractor.extractClass(value);
value = extractClass.attrClass;
if (value) {
attrs[CLASS] = value;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (delete nodeids[instance.id]);
instance.id = value;
nodeids[value] = domNode;
}
instance._emit(prevVal ? EV_ATTRIBUTE_CHANGED : EV_ATTRIBUTE_INSERTED, attributeName, value, prevVal);
// when set in the dom --> quotes need to be set as "
value = value.replace(/"/g, '"');
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
domNode._setAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName, value);
}
else {
domNode._setAttribute(attributeName, value);
}
}
return instance;
},
/**
* Redefines the attributes of both the vnode as well as its related dom-node. The new
* definition replaces any previous attributes (without touching unmodified attributes).
* the `is` attribute cannot be changed for itags.
*
* Syncs the new vnode's attributes with the dom.
*
* @method _setAttrs
* @param newAttrs {Object|Array} the new attributes to be set
* @private
* @chainable
* @since 0.0.1
*/
_setAttrs: function(newAttrs, suppressItagRender) {
// does sync the DOM
var instance = this,
attrsObj, attr, attrs, i, key, keys, len, value;
if (instance.nodeType!==1) {
return;
}
instance._noSync();
attrs = instance.attrs;
attrs.id && (delete nodeids[attrs.id]);
if (Object.isObject(newAttrs)) {
attrsObj = newAttrs;
}
else {
attrsObj = {};
len = newAttrs.length;
for (i=0; i<len; i++) {
attr = newAttrs[i];
attrsObj[attr.name] = attr.value;
}
}
if (instance.isItag && !suppressItagRender) {
if (attrs.is) {
attrsObj.is = attrs.is;
}
else {
delete attrsObj.is;
delete attrsObj.Is;
delete attrsObj.iS;
delete attrsObj.IS;
}
}
// first _remove the attributes that are no longer needed.
// quickest way for object iteration: http://jsperf.com/object-keys-iteration/20
keys = Object.keys(attrs);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
attrsObj[key] || instance._removeAttr(key, suppressItagRender);
}
// next: every attribute that differs: redefine
keys = Object.keys(attrsObj);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
value = attrsObj[key];
(attrs[key]===value) || instance._setAttr(key, value, suppressItagRender);
}
return instance;
},
/**
* Redefines the childNodes of both the vnode as well as its related dom-node. The new
* definition replaces any previous nodes. (without touching unmodified nodes).
*
* Syncs the new vnode's childNodes with the dom.
*
* @method _setChildNodes
* @param newVChildNodes {Array} array with vnodes which represent the new childNodes
* @private
* @chainable
* @since 0.0.1
*/
_setChildNodes: function(newVChildNodes, suppressItagRender, removeSystemElements) {
// does sync the DOM
var instance = this,
vChildNodes = instance.vChildNodes || [],
domNode = instance.domNode,
forRemoval = [],
i, oldChild, newChild, newLength, len, len2, childDomNode, nodeswitch, bkpAttrs, cleanupStyle,
process, bkpChildNodes, needNormalize, prevSuppress, scriptContent, _scripts, scriptLen, j;
instance._noSync();
// first: reset ._vChildren --> by making it empty, its getter will refresh its list on a next call
instance._vChildren = null;
// if newVChildNodes is undefined, then we assume it to be empty --> an empty array
newVChildNodes || (newVChildNodes=[]);
// quickest way to loop through array is by using for loops: http://jsperf.com/array-foreach-vs-for-loop/5
len = vChildNodes.length;
// we need to add the systemNodes -if any- to the new newVChildNodes: they should be retained.
// SystemNodes are always places at the beginning, so they won't get reshuffled:
if (!removeSystemElements) {
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
if (oldChild._systemNode) {
newVChildNodes.insertAt(oldChild, i);
}
else {
break;
}
}
}
newLength = newVChildNodes.length;
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
childDomNode = oldChild.domNode;
if (i < newLength) {
newChild = newVChildNodes[i];
newChild.vParent || (newChild.vParent=instance);
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
/*jshint boss:true */
switch (nodeswitch=NODESWITCH[oldChild.nodeType][newChild.nodeType]) {
/*jshint boss:false */
case 1: // oldNodeType==Element, newNodeType==Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
if ((oldChild.tag!==newChild.tag) ||
((oldChild.tag===newChild.tag) && oldChild.isItag && (oldChild.attrs.is!==newChild.attrs.is))) {
// new tag --> completely replace
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
/*jshint proto:true */
oldChild.isItag && oldChild.domNode.destroyUI && oldChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
newChild.attrs = {}; // reset to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
oldChild._replaceAtParent(newChild);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
else {
// same tag --> only update what is needed
// NOTE: when this._unchangableAttrs exists, an itag-element syncs its UI -->
if (oldChild._data) {
// we might need to set the class `itag-rendered` when the attributeData says so:
// this happens when an itag gets refreshed with an unrendered definition
if (oldChild._data.itagRendered && !newChild.hasClass('itag-rendered')) {
newChild.classNames['itag-rendered'] = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' '+'itag-rendered';
}
else {
newChild.attrs[CLASS] = 'itag-rendered';
}
}
// we might need to set the class `focussed` when the attributeData says so:
// this happens when an itag gets rerendered: its renderFn doesn't know if any elements
// were focussed
if (oldChild._data.focussed && !newChild.hasClass('focussed')) {
newChild.classNames.focussed = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' ' + 'focussed';
}
else {
newChild.attrs[CLASS] = 'focussed';
}
}
if (oldChild._data['fm-tabindex']) {
// node has the tabindex set by the focusmanager,
// but that info might got lost with re-rendering of the new element
newChild.attrs.tabindex = '0';
}
}
if (oldChild.isItag) {
prevSuppress = DOCUMENT._suppressMutationEvents || false;
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
/*jshint proto:true */
newChild.domNode.destroyUI && newChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
oldChild._setAttrs(newChild.attrs, suppressItagRender);
newChild._destroy(true); // destroy through the vnode and removing from DOCUMENT._itagList
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(prevSuppress);
}
else {
oldChild._setAttrs(newChild.attrs, suppressItagRender);
// next: sync the vChildNodes:
oldChild._setChildNodes(newChild.vChildNodes, suppressItagRender);
}
// reset ref. to the domNode, for it might have been changed by newChild:
oldChild.id && (nodeids[oldChild.id]=childDomNode);
newVChildNodes[i] = oldChild;
}
}
break;
case 2: // oldNodeType==Element, newNodeType==TextNode
// case2 and case3 should be treated the same
case 3: // oldNodeType==Element, newNodeType==Comment
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
oldChild._replaceAtParent(newChild);
instance._emit(EV_CONTENT_CHANGE);
break;
case 4: // oldNodeType==TextNode, newNodeType==Element
// case4 and case7 should be treated the same
case 7: // oldNodeType==Comment, newNodeType==Element
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
// oldChild.isVoid = newChild.isVoid;
// delete oldChild.text;
instance._emit(EV_CONTENT_CHANGE);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
break;
case 5: // oldNodeType==TextNode, newNodeType==TextNode
// case5 and case9 should be treated the same
case 9: // oldNodeType==Comment, newNodeType==Comment
if (oldChild.text!==newChild.text) {
oldChild.text = newChild.text;
oldChild.domNode.nodeValue = unescapeEntities(newChild.text);
instance._emit(EV_CONTENT_CHANGE);
}
newVChildNodes[i] = oldChild;
break;
case 6: // oldNodeType==TextNode, newNodeType==Comment
// case6 and case8 should be treated the same
case 8: // oldNodeType==Comment, newNodeType==TextNode
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = oldChild.vParent;
instance._emit(EV_CONTENT_CHANGE);
}
if ((nodeswitch===2) || (nodeswitch===5) || (nodeswitch===8)) {
needNormalize = true;
}
}
else {
// _remove previous definition
_tryRemoveDomNode(domNode, oldChild.domNode);
// the oldChild needs to be removed, however, this cannot be done right now, for it would effect the loop
// so we store it inside a hash to remove it later
forRemoval[forRemoval.length] = oldChild;
}
}
// now definitely remove marked childNodes:
len2 = forRemoval.length;
for (i=0; i<len2; i++) {
forRemoval[i]._destroy();
}
// now we add all new vChildNodes that go beyond `len`:
for (i = len; i < newLength; i++) {
newChild = newVChildNodes[i];
newChild.vParent = instance;
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
switch (newChild.nodeType) {
case 1: // Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
domNode._appendChild(newChild.domNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
break;
case 3: // TextNode
needNormalize = true;
// we need to break through --> no `break`
/* falls through */
default: // TextNode or CommentNode
// newChild.domNode.nodeValue = newChild.text;
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
domNode._appendChild(newChild.domNode);
instance._emit(EV_CONTENT_CHANGE);
}
newChild.storeId();
}
instance.vChildNodes = newVChildNodes;
needNormalize && instance._normalize();
cleanupStyle && instance._cleanupStyle();
return instance;
},
_setUnchangableAttrs: function(unchangableObj) {
this._unchangableAttrs = unchangableObj;
}
};
//---- properties ------------------------------------------------------------------
/**
* A hash of all the `attributes` of the vnode's representing dom-node.
*
* @property attrs
* @type Object
* @since 0.0.1
*/
/**
* Hash with all the classes of the vnode. Every class represents a key, all values are set `true`.
*
* @property classNames
* @type Object
* @since 0.0.1
*/
/**
* The `id` of the vnode's representing dom-node (if any).
*
* @property id
* @type String
* @since 0.0.1
*/
/**
* Tells whether tag is a void Element. Examples are: `br`, `img` and `input`. Non-void Elements are f.e. `div` and `table`.
* For TextNodes and CommentNodes, this property is `undefined`.
*
* @property isVoid
* @type Boolean
* @since 0.0.1
*/
/**
* The `nodeType` of the vnode's representing dom-node (1===ElementNode, 3===TextNode, 8===CommentNode).
*
* @property nodeType
* @type Number
* @since 0.0.1
*/
/**
* The `tag` of the vnode's representing dom-node (allways uppercase).
*
* @property tag
* @type String
* @since 0.0.1
*/
/**
* The `content` of the vnode's representing dom-node, in case it is a TextNode or CommentNode.
* Equals dom-node.nodeValue.
*
* Is `undefined` for ElementNodes.
*
* @property text
* @type String
* @since 0.0.1
*/
/**
* Hash with all the childNodes (vnodes). vChildNodes are any kind of vnodes (nodeType===1, 3 or 8)
*
* @property vChildNodes
* @type Array
* @since 0.0.1
*/
/**
* The underlying `dom-node` that the vnode represents.
*
* @property domNode
* @type domNode
* @since 0.0.1
*/
/**
* vnode's parentNode (defined as a vnode itself).
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
Object.defineProperties(vNodeProto, {
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property innerHTML
* @type String
* @since 0.0.1
*/
innerHTML: {
get: function() {
return this.getHTML();
},
set: function(v) {
this.setHTML(v);
}
},
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property nodeValue
* @type String
* @since 0.0.1
*/
nodeValue: {
get: function() {
var instance = this;
return ((instance.nodeType===3) || (instance.nodeType===8)) ? instance.text : null;
},
set: function(v) {
var instance = this,
newTextContent, prevTextContent;
if ((instance.nodeType===3) || (instance.nodeType===8)) {
prevTextContent = instance.domNode.textContent;
instance.domNode.textContent = v;
// set .text AFTER the dom-node is updated --> the content might be escaped!
newTextContent = instance.text = instance.domNode.textContent;
(newTextContent!==prevTextContent) && instance._emit(EV_CONTENT_CHANGE);
}
}
},
/**
* Gets or sets the outerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property outerHTML
* @type String
* @since 0.0.1
*/
outerHTML: {
get: function() {
return this.getOuterHTML();
},
set: function(v) {
var instance = this,
vParent = instance.vParent,
id = instance.attrs.id,
vnode, vnodes, bkpAttrs, bkpChildNodes, i, len, vChildNodes, isLastChildNode, index, refDomNode;
if ((instance.nodeType!==1) || !vParent) {
return;
}
instance._noSync();
vChildNodes = vParent.vChildNodes;
index = vChildNodes.indexOf(instance);
isLastChildNode = (index===(vChildNodes.length-1));
isLastChildNode || (refDomNode=vChildNodes[index+1].domNode);
vnodes = htmlToVNodes(v, vNodeProto, vParent.ns, vParent);
len = vnodes.length;
if (len>0) {
// the first vnode will replace the current instance:
vnode = vnodes[0];
if (vnode.nodeType===1) {
if (vnode.tag!==instance.tag) {
// new tag --> completely replace
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
id && (delete nodeids[id]);
vnode.attrs = {}; // reset to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
// vnode.attrs = bkpAttrs;
// vnode.vChildNodes = bkpChildNodes;
vnode.id && (nodeids[vnode.id]=vnode.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
else {
instance._setAttrs(vnode.attrs);
instance._setChildNodes(vnode.vChildNodes);
}
}
else {
id && (delete nodeids[id]);
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
}
for (i=1; i<len; i++) {
vnode = vnodes[i];
switch (vnode.nodeType) {
case 1: // Element
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
vnode.attrs = {}; // reset, to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._insertBefore(vnode.domNode, refDomNode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
break;
default: // TextNode or CommentNode
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._appendChild(vnode.domNode, refDomNode);
}
vnode.storeId();
vnode._moveToParent(vParent, index+i);
}
}
},
/**
* Gets or sets the innerContent of the Node as plain text.
*
* The setter syncs with the DOM.
*
* @property textContent
* @type String
* @since 0.0.1
*/
textContent: {
get: function() {
var instance = this,
text = '',
vChildNodes = instance.vChildNodes,
len, i, vChildNode;
if (instance.nodeType===1) {
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
text += (vChildNode.nodeType===3) ? vChildNode.text : ((vChildNode.nodeType===1) ? vChildNode.textContent : '');
}
}
else {
text = instance.text;
}
return text;
},
set: function(v) {
var vnode = Object.create(vNodeProto);
vnode.domNode = DOCUMENT.createTextNode(v);
// create circular reference:
vnode.domNode._vnode = vnode;
vnode.nodeType = 3;
vnode.text = vnode.domNode.textContent;
this._setChildNodes([vnode]);
}
},
/**
* Hash with all the children (vnodes). vChildren are vnodes that have a representing dom-node that is an HtmlElement (nodeType===1)
*
* @property vChildren
* @type Array
* @since 0.0.1
*/
vChildren: {
get: function() {
var instance = this,
children = instance._vChildren,
vChildNode, vChildNodes, i, len;
vChildNodes = instance.vChildNodes;
if (vChildNodes && !children) {
children = instance._vChildren = [];
len = vChildNodes.length;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
(vChildNode.nodeType===1) && (children[children.length]=vChildNode);
}
}
children || (children = instance._vChildren = []);
return children;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirst
* @type vnode
* @since 0.0.1
*/
vFirst: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstChild;
}
},
/**
* Reference to the first vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirstChild
* @type vnode
* @since 0.0.1
*/
vFirstChild: {
get: function() {
return (this.vChildNodes && this.vChildNodes[0]) || null;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vFirstElement
* @type vnode
* @since 0.0.1
*/
vFirstElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstElementChild;
}
},
/**
* Reference to the first vChild, where the related dom-node an Element (nodeType===1).
*
* @property vFirstElementChild
* @type vnode
* @since 0.0.1
*/
vFirstElementChild: {
get: function() {
return this.vChildren[0] || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLast
* @type vnode
* @since 0.0.1
*/
vLast: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastChild;
}
},
/**
* Reference to the last vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLastChild
* @type vnode
* @since 0.0.1
*/
vLastChild: {
get: function() {
var vChildNodes = this.vChildNodes;
return (vChildNodes && vChildNodes[vChildNodes.length-1]) || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vLastElement
* @type vnode
* @since 0.0.1
*/
vLastElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastElementChild;
}
},
/**
* Reference to the last vChild, where the related dom-node an Element (nodeType===1).
*
* @property vLastElementChild
* @type vnode
* @since 0.0.1
*/
vLastElementChild: {
get: function() {
var vChildren = this.vChildren;
return vChildren[vChildren.length-1] || null;
}
},
/**
* the Parent vnode
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
/**
* Reference to the next of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vNext
* @type vnode
* @since 0.0.1
*/
vNext: {
get: function() {
return _findNodeSibling(this, true);
}
},
/**
* Reference to the next of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vNextElement
* @type vnode
* @since 0.0.1
*/
vNextElement: {
get: function() {
return _findElementSibling(this, true);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vPrevious
* @type vnode
* @since 0.0.1
*/
vPrevious: {
get: function() {
return _findNodeSibling(this);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vPreviousElement
* @type vnode
* @since 0.0.1
*/
vPreviousElement: {
get: function() {
return _findElementSibling(this);
}
}
});
return vNodeProto;
};
},{"./attribute-extractor.js":4233,"./html-parser.js":4237,"./vdom-ns.js":4239,"js-ext/extra/hashmap.js":4191,"js-ext/extra/lightmap.js":4192,"js-ext/lib/array.js":4193,"js-ext/lib/object.js":4194,"js-ext/lib/string.js":4196,"polyfill":4213,"utils/lib/timers.js":4216}],4241:[function(require,module,exports){
arguments[4][100][0].apply(exports,arguments)
},{"./partials/extend-document.js":4235,"./partials/extend-element.js":4236,"./partials/node-parser.js":4238,"js-ext/extra/hashmap.js":4191,"js-ext/lib/object.js":4194,"utils/lib/timers.js":4216}],4242:[function(require,module,exports){
module.exports=require(14)
},{}],4243:[function(require,module,exports){
module.exports=require(15)
},{}],4244:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4242,"./lib/window.console.js":4243}],4245:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4246,"./lib/timers.js":4247}],4246:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4061,"polyfill/polyfill-base.js":4250}],4247:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4250}],4248:[function(require,module,exports){
module.exports=require(14)
},{}],4249:[function(require,module,exports){
module.exports=require(15)
},{}],4250:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4248,"./lib/window.console.js":4249}],4251:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":4252}],4252:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":4253,"js-ext/lib/object.js":4254}],4253:[function(require,module,exports){
module.exports=require(4)
},{}],4254:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4253,"polyfill/polyfill-base.js":4257,"utils":4258}],4255:[function(require,module,exports){
module.exports=require(14)
},{}],4256:[function(require,module,exports){
module.exports=require(15)
},{}],4257:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4255,"./lib/window.console.js":4256}],4258:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4259,"./lib/timers.js":4260}],4259:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4253,"polyfill/polyfill-base.js":4263}],4260:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4263}],4261:[function(require,module,exports){
module.exports=require(14)
},{}],4262:[function(require,module,exports){
module.exports=require(15)
},{}],4263:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4261,"./lib/window.console.js":4262}],4264:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":4268,"js-ext/extra/hashmap.js":4265,"polyfill/polyfill-base.js":4273}],4265:[function(require,module,exports){
module.exports=require(4)
},{}],4266:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":4267,"../lib/object.js":4268,"./classes.js":4264,"js-ext/extra/hashmap.js":4265,"polyfill/lib/weakmap.js":4271}],4267:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":4273}],4268:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4265,"polyfill/polyfill-base.js":4273,"utils":4274}],4269:[function(require,module,exports){
module.exports=require(29)
},{}],4270:[function(require,module,exports){
module.exports=require(14)
},{}],4271:[function(require,module,exports){
module.exports=require(57)
},{}],4272:[function(require,module,exports){
module.exports=require(15)
},{}],4273:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4270,"./lib/window.console.js":4272}],4274:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4275,"./lib/timers.js":4276}],4275:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4265,"polyfill/polyfill-base.js":4279}],4276:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4279}],4277:[function(require,module,exports){
module.exports=require(14)
},{}],4278:[function(require,module,exports){
module.exports=require(15)
},{}],4279:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4277,"./lib/window.console.js":4278}],4280:[function(require,module,exports){
arguments[4][5][0].apply(exports,arguments)
},{"event-dom":4281,"js-ext/extra/classes.js":4376,"js-ext/extra/hashmap.js":4377,"js-ext/lib/object.js":4378,"js-ext/lib/promise.js":4379,"js-ext/lib/string.js":4380,"polyfill":4392,"utils/lib/timers.js":4393,"vdom":4449}],4281:[function(require,module,exports){
module.exports=require(267)
},{"event":4285,"js-ext/extra/hashmap.js":4301,"js-ext/lib/array.js":4302,"js-ext/lib/object.js":4303,"js-ext/lib/string.js":4304,"polyfill/polyfill-base.js":4316,"utils":4317,"vdom":4375}],4282:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":4287,"js-ext/lib/object.js":4288,"polyfill/polyfill-base.js":4300}],4283:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":4282}],4284:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":4282,"js-ext/extra/classes.js":4286,"js-ext/lib/object.js":4288}],4285:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":4282,"./event-emitter.js":4283,"./event-listener.js":4284}],4286:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":4288,"js-ext/extra/hashmap.js":4287,"polyfill/polyfill-base.js":4291}],4287:[function(require,module,exports){
module.exports=require(4)
},{}],4288:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4287,"polyfill/polyfill-base.js":4291,"utils":4292}],4289:[function(require,module,exports){
module.exports=require(14)
},{}],4290:[function(require,module,exports){
module.exports=require(15)
},{}],4291:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4289,"./lib/window.console.js":4290}],4292:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4293,"./lib/timers.js":4294}],4293:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4287,"polyfill/polyfill-base.js":4297}],4294:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4297}],4295:[function(require,module,exports){
module.exports=require(14)
},{}],4296:[function(require,module,exports){
module.exports=require(15)
},{}],4297:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4295,"./lib/window.console.js":4296}],4298:[function(require,module,exports){
module.exports=require(14)
},{}],4299:[function(require,module,exports){
module.exports=require(15)
},{}],4300:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4298,"./lib/window.console.js":4299}],4301:[function(require,module,exports){
module.exports=require(4)
},{}],4302:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":4307}],4303:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4301,"polyfill/polyfill-base.js":4307,"utils":4308}],4304:[function(require,module,exports){
module.exports=require(29)
},{}],4305:[function(require,module,exports){
module.exports=require(14)
},{}],4306:[function(require,module,exports){
module.exports=require(15)
},{}],4307:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4305,"./lib/window.console.js":4306}],4308:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4309,"./lib/timers.js":4310}],4309:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4301,"polyfill/polyfill-base.js":4313}],4310:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4313}],4311:[function(require,module,exports){
module.exports=require(14)
},{}],4312:[function(require,module,exports){
module.exports=require(15)
},{}],4313:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4311,"./lib/window.console.js":4312}],4314:[function(require,module,exports){
module.exports=require(14)
},{}],4315:[function(require,module,exports){
module.exports=require(15)
},{}],4316:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4314,"./lib/window.console.js":4315}],4317:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4318,"./lib/timers.js":4319}],4318:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4301,"polyfill/polyfill-base.js":4322}],4319:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4322}],4320:[function(require,module,exports){
module.exports=require(14)
},{}],4321:[function(require,module,exports){
module.exports=require(15)
},{}],4322:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4320,"./lib/window.console.js":4321}],4323:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],4324:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":4328,"js-ext/extra/hashmap.js":4325,"polyfill/polyfill-base.js":4334}],4325:[function(require,module,exports){
module.exports=require(4)
},{}],4326:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":4327,"../lib/object.js":4328,"./classes.js":4324,"js-ext/extra/hashmap.js":4325,"polyfill/lib/weakmap.js":4332}],4327:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":4334}],4328:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4325,"polyfill/polyfill-base.js":4334,"utils":4335}],4329:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":4334}],4330:[function(require,module,exports){
module.exports=require(29)
},{}],4331:[function(require,module,exports){
module.exports=require(14)
},{}],4332:[function(require,module,exports){
module.exports=require(57)
},{}],4333:[function(require,module,exports){
module.exports=require(15)
},{}],4334:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4331,"./lib/window.console.js":4333}],4335:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4336,"./lib/timers.js":4337}],4336:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4325,"polyfill/polyfill-base.js":4340}],4337:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4340}],4338:[function(require,module,exports){
module.exports=require(14)
},{}],4339:[function(require,module,exports){
module.exports=require(15)
},{}],4340:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4338,"./lib/window.console.js":4339}],4341:[function(require,module,exports){
module.exports=require(66)
},{}],4342:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":4341}],4343:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":4341}],4344:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":4341}],4345:[function(require,module,exports){
module.exports=require(14)
},{}],4346:[function(require,module,exports){
module.exports=require(15)
},{}],4347:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4345,"./lib/window.console.js":4346}],4348:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4349,"./lib/timers.js":4350}],4349:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4325,"polyfill/polyfill-base.js":4353}],4350:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4353}],4351:[function(require,module,exports){
module.exports=require(14)
},{}],4352:[function(require,module,exports){
module.exports=require(15)
},{}],4353:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4351,"./lib/window.console.js":4352}],4354:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":4355}],4355:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":4356,"js-ext/lib/object.js":4357}],4356:[function(require,module,exports){
module.exports=require(4)
},{}],4357:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4356,"polyfill/polyfill-base.js":4360,"utils":4361}],4358:[function(require,module,exports){
module.exports=require(14)
},{}],4359:[function(require,module,exports){
module.exports=require(15)
},{}],4360:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4358,"./lib/window.console.js":4359}],4361:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4362,"./lib/timers.js":4363}],4362:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4356,"polyfill/polyfill-base.js":4366}],4363:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4366}],4364:[function(require,module,exports){
module.exports=require(14)
},{}],4365:[function(require,module,exports){
module.exports=require(15)
},{}],4366:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4364,"./lib/window.console.js":4365}],4367:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":4325,"js-ext/lib/object.js":4328,"js-ext/lib/string.js":4330,"polyfill":4347,"polyfill/extra/transition.js":4342,"polyfill/extra/vendorCSS.js":4344}],4368:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":4325,"js-ext/lib/object.js":4328,"polyfill":4347}],4369:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":4325,"js-ext/lib/object.js":4328,"js-ext/lib/string.js":4330,"polyfill":4347}],4370:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":4323,"./attribute-extractor.js":4367,"./element-array.js":4368,"./html-parser.js":4371,"./node-parser.js":4372,"./vdom-ns.js":4373,"./vnode.js":4374,"js-ext/extra/hashmap.js":4325,"js-ext/lib/object.js":4328,"js-ext/lib/promise.js":4329,"js-ext/lib/string.js":4330,"polyfill":4347,"polyfill/extra/transition.js":4342,"polyfill/extra/transitionend.js":4343,"polyfill/extra/vendorCSS.js":4344,"utils":4348,"window-ext":4354}],4371:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":4367,"./vdom-ns.js":4373,"js-ext/extra/hashmap.js":4325,"js-ext/lib/object.js":4328,"polyfill":4347}],4372:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":4367,"./vdom-ns.js":4373,"./vnode.js":4374,"js-ext/extra/hashmap.js":4325,"js-ext/lib/object.js":4328,"polyfill":4347}],4373:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":4325,"js-ext/lib/object.js":4328,"polyfill":4347}],4374:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":4367,"./html-parser.js":4371,"./vdom-ns.js":4373,"js-ext/extra/hashmap.js":4325,"js-ext/extra/lightmap.js":4326,"js-ext/lib/array.js":4327,"js-ext/lib/object.js":4328,"js-ext/lib/string.js":4330,"polyfill":4347,"utils/lib/timers.js":4350}],4375:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":4369,"./partials/extend-element.js":4370,"./partials/node-parser.js":4372,"js-ext/extra/hashmap.js":4325,"js-ext/lib/object.js":4328,"utils/lib/timers.js":4350}],4376:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":4378,"js-ext/extra/hashmap.js":4377,"polyfill/polyfill-base.js":4383}],4377:[function(require,module,exports){
module.exports=require(4)
},{}],4378:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4377,"polyfill/polyfill-base.js":4383,"utils":4384}],4379:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":4383}],4380:[function(require,module,exports){
module.exports=require(29)
},{}],4381:[function(require,module,exports){
module.exports=require(14)
},{}],4382:[function(require,module,exports){
module.exports=require(15)
},{}],4383:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4381,"./lib/window.console.js":4382}],4384:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4385,"./lib/timers.js":4386}],4385:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4377,"polyfill/polyfill-base.js":4389}],4386:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4389}],4387:[function(require,module,exports){
module.exports=require(14)
},{}],4388:[function(require,module,exports){
module.exports=require(15)
},{}],4389:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4387,"./lib/window.console.js":4388}],4390:[function(require,module,exports){
module.exports=require(14)
},{}],4391:[function(require,module,exports){
module.exports=require(15)
},{}],4392:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4390,"./lib/window.console.js":4391}],4393:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4396}],4394:[function(require,module,exports){
module.exports=require(14)
},{}],4395:[function(require,module,exports){
module.exports=require(15)
},{}],4396:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4394,"./lib/window.console.js":4395}],4397:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],4398:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":4402,"js-ext/extra/hashmap.js":4399,"polyfill/polyfill-base.js":4408}],4399:[function(require,module,exports){
module.exports=require(4)
},{}],4400:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":4401,"../lib/object.js":4402,"./classes.js":4398,"js-ext/extra/hashmap.js":4399,"polyfill/lib/weakmap.js":4406}],4401:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":4408}],4402:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4399,"polyfill/polyfill-base.js":4408,"utils":4409}],4403:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":4408}],4404:[function(require,module,exports){
module.exports=require(29)
},{}],4405:[function(require,module,exports){
module.exports=require(14)
},{}],4406:[function(require,module,exports){
module.exports=require(57)
},{}],4407:[function(require,module,exports){
module.exports=require(15)
},{}],4408:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4405,"./lib/window.console.js":4407}],4409:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4410,"./lib/timers.js":4411}],4410:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4399,"polyfill/polyfill-base.js":4414}],4411:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4414}],4412:[function(require,module,exports){
module.exports=require(14)
},{}],4413:[function(require,module,exports){
module.exports=require(15)
},{}],4414:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4412,"./lib/window.console.js":4413}],4415:[function(require,module,exports){
module.exports=require(66)
},{}],4416:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":4415}],4417:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":4415}],4418:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":4415}],4419:[function(require,module,exports){
module.exports=require(14)
},{}],4420:[function(require,module,exports){
module.exports=require(15)
},{}],4421:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4419,"./lib/window.console.js":4420}],4422:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4423,"./lib/timers.js":4424}],4423:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4399,"polyfill/polyfill-base.js":4427}],4424:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4427}],4425:[function(require,module,exports){
module.exports=require(14)
},{}],4426:[function(require,module,exports){
module.exports=require(15)
},{}],4427:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4425,"./lib/window.console.js":4426}],4428:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":4429}],4429:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":4430,"js-ext/lib/object.js":4431}],4430:[function(require,module,exports){
module.exports=require(4)
},{}],4431:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4430,"polyfill/polyfill-base.js":4434,"utils":4435}],4432:[function(require,module,exports){
module.exports=require(14)
},{}],4433:[function(require,module,exports){
module.exports=require(15)
},{}],4434:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4432,"./lib/window.console.js":4433}],4435:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4436,"./lib/timers.js":4437}],4436:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4430,"polyfill/polyfill-base.js":4440}],4437:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4440}],4438:[function(require,module,exports){
module.exports=require(14)
},{}],4439:[function(require,module,exports){
module.exports=require(15)
},{}],4440:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4438,"./lib/window.console.js":4439}],4441:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":4399,"js-ext/lib/object.js":4402,"js-ext/lib/string.js":4404,"polyfill":4421,"polyfill/extra/transition.js":4416,"polyfill/extra/vendorCSS.js":4418}],4442:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":4399,"js-ext/lib/object.js":4402,"polyfill":4421}],4443:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":4399,"js-ext/lib/object.js":4402,"js-ext/lib/string.js":4404,"polyfill":4421}],4444:[function(require,module,exports){
arguments[4][95][0].apply(exports,arguments)
},{"../css/element.css":4397,"./attribute-extractor.js":4441,"./element-array.js":4442,"./html-parser.js":4445,"./node-parser.js":4446,"./vdom-ns.js":4447,"./vnode.js":4448,"js-ext/extra/hashmap.js":4399,"js-ext/lib/object.js":4402,"js-ext/lib/promise.js":4403,"js-ext/lib/string.js":4404,"polyfill":4421,"polyfill/extra/transition.js":4416,"polyfill/extra/transitionend.js":4417,"polyfill/extra/vendorCSS.js":4418,"utils":4422,"window-ext":4428}],4445:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":4441,"./vdom-ns.js":4447,"js-ext/extra/hashmap.js":4399,"js-ext/lib/object.js":4402,"polyfill":4421}],4446:[function(require,module,exports){
arguments[4][97][0].apply(exports,arguments)
},{"./attribute-extractor.js":4441,"./vdom-ns.js":4447,"./vnode.js":4448,"js-ext/extra/hashmap.js":4399,"js-ext/lib/object.js":4402,"polyfill":4421}],4447:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":4399,"js-ext/lib/object.js":4402,"polyfill":4421}],4448:[function(require,module,exports){
"use strict";
/**
* Delivers the `vnode` prototype object, which is a virtualisation of an `Element` inside the Dom.
* These Elements work smoothless with the vdom (see ...).
*
* vnodes are much quicker to access and walk through than native dom-nodes. However, this is a module you don't need
* by itself: `Element`-types use these features under the hood.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* <br>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
*
* @module vdom
* @submodule vnode
* @class vnode
* @since 0.0.1
*/
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('js-ext/lib/string.js');
require('polyfill');
var createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.VNode) {
return window._ITSAmodules.VNode; // VNODE was already created
}
console.warn('VDOM 6');
var NS = require('./vdom-ns.js')(window),
extractor = require('./attribute-extractor.js')(window),
DOCUMENT = window.document,
LightMap = require('js-ext/extra/lightmap.js'),
MUTATION_EVENTS = new LightMap(),
BATCH_WILL_RUN = false,
xmlNS = NS.xmlNS,
nodeids = NS.nodeids,
htmlToVNodes = require('./html-parser.js')(window),
timers = require('utils/lib/timers.js'),
async = timers.async,
later = timers.later,
/*jshint proto:true */
PROTO_SUPPORTED = !!Object.__proto__,
/*jshint proto:false */
// cleanup memory after 1 minute: removed nodes SHOULD NOT be accessed afterwards
// because vnode would be recalculated and might be different from before
DESTROY_DELAY = 60000,
unescapeEntities = NS.UnescapeEntities,
NTH_CHILD_REGEXP = /^(?:(\d*)[n|N])([\+|\-](\d+))?$/, // an+b
STRING = 'string',
CLASS = 'class',
STYLE = 'style',
ID = 'id',
NODE= 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
EV_REMOVED = NODE+REMOVE,
EV_INSERTED = NODE+INSERT,
EV_CONTENT_CHANGE = NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = ATTRIBUTE+INSERT,
SPLIT_CHARACTER = createHashMap({
' ': true,
'>': true,
'+': true, // only select the element when it is immediately preceded by the former element
'~': true // only the element when it has the former element as a sibling. (just like `+`, but less strict)
}),
STORABLE_SPLIT_CHARACTER = createHashMap({
'>': true,
'+': true,
'~': true
}),
SIBLING_MATCH_CHARACTER = createHashMap({
'+': true,
'~': true
}),
ATTR_DETAIL_SPECIFIERS = createHashMap({
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to attribute-name end-tokens.
*
* @property END_ATTRIBUTENAME
* @default {
* '=': true,
* ']': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
END_ATTRIBUTENAME = createHashMap({
'=': true,
']': true,
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to different changes of Element nodeType changes.
*
* @property NODESWITCH
* @default {
* 1: {
* 1: 1,
* 3: 2,
* 8: 3
* },
* 3: {
* 1: 4,
* 3: 5,
* 8: 6
* },
* 8: {
* 1: 7,
* 3: 8,
* 8: 9
* }
* }
* @type Object
* @protected
* @since 0.0.1
*/
NODESWITCH = createHashMap({
1: createHashMap({
1: 1, // oldNodeType==Element, newNodeType==Element
3: 2, // oldNodeType==Element, newNodeType==TextNode
8: 3 // oldNodeType==Element, newNodeType==Comment
}),
3: createHashMap({
1: 4, // oldNodeType==TextNode, newNodeType==Element
3: 5, // oldNodeType==TextNode, newNodeType==TextNode
8: 6 // oldNodeType==TextNode, newNodeType==Comment
}),
8: createHashMap({
1: 7, // oldNodeType==Comment, newNodeType==Element
3: 8, // oldNodeType==Comment, newNodeType==TextNode
8: 9 // oldNodeType==Comment, newNodeType==Comment
})
}),
/**
* Object to gain quick access to selector start-tokens.
*
* @property SELECTOR_IDENTIFIERS
* @default {
* '#': 1,
* '.': 2,
* '[': 3
* }
* @type Object
* @protected
* @since 0.0.1
*/
SELECTOR_IDENTIFIERS = createHashMap({
'#': 1,
'.': 2,
'[': 3,
':': 4
}),
PSEUDO_FIRST_CHILD = ':first-child',
PSEUDO_FIRST_OF_TYPE = ':first-of-type',
PSEUDO_LAST_CHILD = ':last-child',
PSEUDO_LAST_OF_TYPE = ':last-of-type',
PSEUDO_NTH_CHILD = ':nth-child',
PSEUDO_NTH_LAST_CHILD = ':nth-last-child',
PSEUDO_NTH_LAST_OF_TYPE = ':nth-last-of-type',
PSEUDO_NTH_OF_TYPE = ':nth-of-type',
PSEUDO_ONLY_OF_TYPE = ':only-of-type',
PSEUDO_ONLY_CHILD = ':only-child',
/**
* Object to gain quick access to the selectors that required children
*
* @property PSEUDO_REQUIRED_CHILDREN
* @default {
* ':first-child': true,
* ':first-of-type': true,
* ':last-child': true,
* ':last-of-type': true,
* ':nth-child': true,
* ':nth-last-child': true,
* ':nth-last-of-type': true,
* ':nth-of-type': true,
* ':only-of-type': true,
* ':only-child': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
PSEUDO_REQUIRED_CHILDREN = createHashMap(),
_matchesSelectorItem, _matchesOneSelector, _findElementSibling, vNodeProto, _markRemoved, _tryReplaceChild,
_splitSelector, _findNodeSibling, _matchNthChild, _batchEmit, _emitDestroyChildren, _tryRemoveDomNode;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_CHILD] = true;
/**
* Searches for the next -or previous- node-sibling (nodeType of 1, 3 or 8).
*
* @method _findNodeSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findNodeSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
index = vParent.vChildNodes.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildNodes[index];
};
/**
* Searches for the next -or previous- Element-sibling (nodeType of 1).
*
* @method _findElementSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findElementSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
if (vnode.nodeType===1) {
index = vParent.vChildren.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildren[index];
}
else {
/*jshint noempty:true */
while ((vnode=_findNodeSibling(vnode, next)) && (vnode.nodeType!==1)) {}
/*jshint noempty:false */
return vnode;
}
};
/**
* Check whether the vnode matches a "nth-child" test, which is used for css pseudoselectors like `nth-child`, `nth-of-type` etc.
*
* @method _matchNthChild
* @param pseudoArg {String} the argument for nth-child
* @param index {Number} the index of the inspected vnode
* @return {Boolean} whether the vnode matches the nthChild test
* @protected
* @private
* @since 0.0.1
*/
_matchNthChild = function(pseudoArg, index) {
var match, k, a, b, nodeOk, nthIndex, sign, isNumber;
(pseudoArg==='even') && (pseudoArg='2n');
(pseudoArg==='odd') && (pseudoArg='2n+1');
match = pseudoArg.match(NTH_CHILD_REGEXP) || (isNumber=pseudoArg.validateNumber());
if (!match) {
return false;
}
// pseudoArg follows the pattern: `an+b`
if (!isNumber) {
a = match[1];
sign = match[2];
b = match[3] || 0;
(b==='') && (b=0);
}
else {
b = pseudoArg;
}
sign && (sign=sign[0]);
if (!a) {
// only fixed index to match
return (sign==='-') ? false : (parseInt(b, 10)===index);
}
else {
// we need to iterate
nodeOk = false;
b = window.Number(b);
for (k=0; !nodeOk; k++) {
nthIndex = (sign==='-') ? (a*k) - b : (a*k) + b;
if (nthIndex===index) {
nodeOk = true;
}
else if (nthIndex>index) {
// beyond index --> will never become a fix anymore
return false;
}
}
return nodeOk;
}
};
/**
* Check whether the vnode matches the css-selector. the css-selector should be a single selector,
* not multiple, so it shouldn't contain a `comma`.
*
* @method _matchesOneSelector
* @param vnode {vnode} the vnode to inspect
* @param selector {String} the selector-item to check the match for
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches the css-selector
* @protected
* @private
* @since 0.0.1
*/
_matchesOneSelector = function(vnode, selector, relatedVNode) {
var selList = _splitSelector(selector),
size = selList.length,
selMatch = false,
i, selectorItem, last,
rightvnode, relationMatch, checkRelation;
if (STORABLE_SPLIT_CHARACTER[selList[size-1]]) {
return false;
}
relationMatch = function(leftVNode, rightVNode, relationCharacter) {
var match, vParent, vChildren;
// when `selector` starts with `>`, `~` or `+`, then
// there should also be a match comparing a related node!
switch (relationCharacter) {
case '>':
leftVNode || (leftVNode=rightVNode.vParent);
match = (leftVNode.vChildren.indexOf(rightVNode)!==-1);
break;
case '~':
leftVNode || (leftVNode=rightVNode.vFirstElement);
vParent = leftVNode.vParent;
vChildren = vParent && vParent.vChildren;
match = vParent && (vChildren.indexOf(leftVNode)<vChildren.indexOf(rightVNode));
break;
case '+':
leftVNode || (leftVNode=rightVNode.vPreviousElement);
match = (leftVNode.vNextElement === rightVNode);
}
return !!match;
};
last = size - 1;
i = last;
while (vnode && ((selMatch || (i===last)) && (i>=0))) {
selectorItem = selList[i];
if (STORABLE_SPLIT_CHARACTER[selectorItem]) {
checkRelation = selectorItem;
i--;
}
else {
selMatch = _matchesSelectorItem(vnode, selectorItem);
if (!selMatch && (i===last)) {
return false;
}
while (!selMatch && vnode && (i!==last)) {
// may also look at nodes higher in the chain
vnode = SIBLING_MATCH_CHARACTER[selList[i+1]] ? vnode.vPreviousElement : vnode.vParent;
selMatch = vnode && _matchesSelectorItem(vnode, selectorItem);
}
selMatch && checkRelation && vnode && (selMatch=relationMatch(vnode, rightvnode, checkRelation));
rightvnode = vnode;
if (vnode) {
// do a precheck on the next checkRelation:
// and determine whether to set the vnode upper the tree or at the previous sibling:
vnode = SIBLING_MATCH_CHARACTER[selList[i-1]] ? vnode.vPreviousElement : vnode.vParent;
}
if (selMatch) {
checkRelation = false;
i--;
}
}
}
if ((rightvnode===relatedVNode) || (i!==-1)) {
selMatch = false;
}
selMatch && checkRelation && rightvnode && (selMatch=relationMatch(relatedVNode, rightvnode, checkRelation));
return selMatch;
};
/**
* Check whether the vnode matches one specific selector-item. Suppose the css-selector: "#mynode li.red .blue"
* then there are 3 selector-items: "#mynode", "li.red" and ".blue"
*
* This method also can handle the new selectors:
* <ul>
* <li>[att^=val] –-> the “begins with” selector</li>
* <li>[att$=val] –-> the “ends with” selector</li>
* <li>[att*=val] –-> the “contains” selector (might be a substring)</li>
* <li>[att~=val] –-> the “contains” selector as a separate word, separated by spaces</li>
* <li>[att|=val] –-> the “contains” selector as a separate word, separated by `|`</li>
* <li>+ --> (same level)</li>
* <li>~ --> (same level)</li>
* </ul>
*
* @method _matchesSelectorItem
* @param vnode {Object} the vnode to inspect
* @param selectorItem {String} the selector-item to check the match for
* @return {Boolean} whether the vnode matches the selector-item
* @protected
* @private
* @since 0.0.1
*/
_matchesSelectorItem = function (vnode, selectorItem) {
var i = 0,
len = selectorItem.length,
character = selectorItem[0],
tagName, id, className, attributeName, attributeValue, stringMarker, attributeisString, isBoolean, insideAttributeValue, insideAttribute,
vParent, checkBoolean, treatment, k, min, max, value, len2, index, found, pseudo, pseudoArg, arglevel, count, vParentVChildren;
if (selectorItem==='*') {
return true;
}
if (!SELECTOR_IDENTIFIERS[character]) {
// starts with tagName
tagName = '';
// reposition i to continue in the right way:
i--;
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
tagName += character;
}
if (tagName.toUpperCase()!==vnode.tag) {
return false;
}
}
while (i<len) {
switch (character) {
case '#':
id = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
id += character;
}
if (id!==vnode.id) {
return false;
}
break;
case '.':
className = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
className += character;
}
if (!vnode.hasClass(className)) {
return false;
}
break;
case '[':
attributeName = '';
while ((++i<len) && (character=selectorItem[i]) && !END_ATTRIBUTENAME[character]) {
attributeName += character;
}
// if character===']' then we have an attribute without a value-definition
if (!vnode.attrs[attributeName] || ((character===']') && (vnode.attrs[attributeName]!==''))) {
return !!vnode.attrs[attributeName];
}
// now we read the value of the attribute
// however, it could be that the selector has a special `detailed` identifier set (defined by: ATTR_DETAIL_SPECIFIERS)
if (ATTR_DETAIL_SPECIFIERS[character]) {
treatment = character; // store the character to know how the attributedata should be treaded
i++; // character should be a "=" by now
}
else {
treatment = null;
}
attributeValue = '';
stringMarker = selectorItem[i+1];
attributeisString = (stringMarker==='"') || (stringMarker==="'");
attributeisString && (i++);
// end of attributaValue = (character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker))
while ((++i<len) && (character=selectorItem[i]) && !((character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker)))) {
attributeValue += character;
}
if (attributeisString) {
// if attribute is string, then we need to _remove to last stringmarker
attributeValue = attributeValue.substr(0, attributeValue.length-1);
}
else {
// if attribute is no string, then we need to typecast its value
isBoolean = ((attributeValue.length>3) && (attributeValue.length<6) &&
(checkBoolean=attributeValue.toUpperCase()) &&
((checkBoolean==='FALSE') || (checkBoolean==='TRUE')));
// typecast the value to either Boolean or Number:
attributeValue = isBoolean ? (checkBoolean==='TRUE') : parseFloat(attributeValue);
}
// depending upon how the attributedata should be treated:
if (treatment) {
switch (treatment) {
case '^': // “begins with” selector
if (!vnode.attrs[attributeName].startsWith(attributeValue)) {
return false;
}
break;
case '$': // “ends with” selector
if (!vnode.attrs[attributeName].endsWith(attributeValue)) {
return false;
}
break;
case '*': // “contains” selector (might be a substring)
if (!vnode.attrs[attributeName].contains(attributeValue)) {
return false;
}
break;
case '~': // “contains” selector as a separate word, separated by spaces
if (!(' '+vnode.attrs[attributeName]+' ').contains(' '+attributeValue+' ')) {
return false;
}
break;
case '|': // “contains” selector as a separate word, separated by `|`
if (!('|'+vnode.attrs[attributeName]+'|').contains('|'+attributeValue+'|')) {
return false;
}
break;
}
}
else if (vnode.attrs[attributeName]!==attributeValue) {
return false;
}
// we still need to increase one position:
(++i<len) && (character=selectorItem[i]);
break;
case ':':
// we have a pseudo-selector
// first, find out which one
// because '::' is a valid start (though without any selection), we start to back the next character as well:
pseudo = ':'+selectorItem[++i];
pseudoArg = '';
vParent = vnode.vParent;
vParentVChildren = vParent && vParent.vChildren;
// pseudo-selectors might have an argument passed in, like `:nth-child(2n+1)` or `:not([type="checkbox"])` --> we
// store this argument inside `pseudoArg`
// also note that combinations are possible with `:not` --> `:not(:nth-child(2n+1))`
// also note that we cannot "just" look for a closing character when running into the usage of attributes:
// for example --> `:not([data-x="some data :)"])`
// that's why -once we are inside attribute-data- we need to continue until the attribute-data ends
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
if (character==='(') {
// starting arguments
arglevel = 1;
insideAttribute = false;
insideAttributeValue = false;
while ((++i<len) && (character=selectorItem[i]) && (arglevel>0)) {
if (!insideAttribute) {
if (character==='(') {
arglevel++;
}
else if (character===')') {
arglevel--;
}
else if (character==='[') {
insideAttribute = true;
}
}
else {
// inside attribute
if (!insideAttributeValue) {
if ((character==='"') || (character==="'")) {
insideAttributeValue = true;
stringMarker = character;
}
else if (character===']') {
insideAttribute = false;
}
}
else if ((character===stringMarker) && (selectorItem[i+1]===']')) {
insideAttributeValue = false;
}
}
(arglevel>0) && (pseudoArg+=character);
}
}
else {
pseudo += character;
}
}
// now, `pseudo` is known as well as its possible pseudoArg
if (!vParentVChildren && PSEUDO_REQUIRED_CHILDREN[pseudo]) {
return false;
}
switch (pseudo) {
case ':checked': // input:checked Selects every checked <input> element
if (!vnode.attrs.checked) {
return false;
}
break;
case ':disabled': // input:disabled Selects every disabled <input> element
if (!vnode.attrs.disabled) {
return false;
}
break;
case ':empty': // p:empty Selects every <p> element that has no children (including text nodes)
if (vnode.vChildNodes && (vnode.vChildNodes.length>0)) {
return false;
}
break;
case ':enabled': // input:enabled Selects every enabled <input> element
if (vnode.attrs.disabled) {
return false;
}
break;
case PSEUDO_FIRST_CHILD: // p:first-child Selects every <p> element that is the first child of its parent
if (vParentVChildren[0]!==vnode) {
return false;
}
break;
case PSEUDO_FIRST_OF_TYPE: // p:first-of-type Selects every <p> element that is the first <p> element of its parent
for (k=vParentVChildren.indexOf(vnode)-1; k>=0; k--) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':focus': // input:focus Selects the input element which has focus
if (vnode.domNode!==DOCUMENT.activeElement) {
return false;
}
break;
case ':in-range': // input:in-range Selects input elements with a value within a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || (value<min) || (value>max)) {
return false;
}
break;
case ':lang': // p:lang(it) Selects every <p> element with a lang attribute equal to "it" (Italian)
if (vnode.attrs.lang!==pseudoArg) {
return false;
}
break;
case PSEUDO_LAST_CHILD: // p:last-child Selects every <p> element that is the last child of its parent
if (vParentVChildren[vParentVChildren.length-1]!==vnode) {
return false;
}
break;
case PSEUDO_LAST_OF_TYPE: // p:last-of-type Selects every <p> element that is the last <p> element of its parent
len2 = vParentVChildren.length;
for (k=vParentVChildren.indexOf(vnode)+1; k<len2; k++) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':not': // :not(p) Selects every element that is not a <p> element
if (vnode.matchesSelector(pseudoArg)) {
return false;
}
break;
case PSEUDO_NTH_CHILD: // p:nth-child(2) Selects every <p> element that is the second child of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
index = vParentVChildren.indexOf(vnode)+1;
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_CHILD: // p:nth-last-child(2) Selects every <p> element that is the second child of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_OF_TYPE: // p:nth-last-of-type(2) Selects every <p> element that is the second <p> element of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
index = 0;
for (k=vParentVChildren.length-1; (k>=0) && !found; k--) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_OF_TYPE: // p:nth-of-type(2) Selects every <p> element that is the second <p> element of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
len2 = vParentVChildren.length;
index = 0;
for (k=0; (k<len2) && !found; k++) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_ONLY_OF_TYPE: // p:only-of-type Selects every <p> element that is the only <p> element of its parent
len2 = vParentVChildren.length;
count = 0;
for (k=0; (k<len2) && (count<=1); k++) {
(vParentVChildren[k].tag===vnode.tag) && count++;
}
if (count!==1) {
return false;
}
break;
case PSEUDO_ONLY_CHILD: // p:only-child Selects every <p> element that is the only child of its parent
if (vParentVChildren.length!==1) {
return false;
}
break;
case ':optional': // input:optional Selects input elements with no "required" attribute
if (vnode.attrs.required) {
return false;
}
break;
case ':out-of-range': // input:out-of-range Selects input elements with a value outside a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || ((value>=min) && (value<=max))) {
return false;
}
break;
case ':read-only': // input:read-only Selects input elements with the "readonly" attribute specified
if (!vnode.attrs.readonly) {
return false;
}
break;
case ':read-write': // input:read-write Selects input elements with the "readonly" attribute NOT specified
if (vnode.attrs.readonly) {
return false;
}
break;
case ':required': // input:required Selects input elements with the "required" attribute specified
if (!vnode.attrs.required) {
return false;
}
break;
case ':root': // Selects the document's root element
if (vnode.domNode!==DOCUMENT.documentElement) {
return false;
}
break;
}
}
}
return true;
};
/**
* Splits the selector into separate subselector-items that should match different elements through the tree.
* Special characters '>' and '+' are added as separate items in the hash.
*
* @method _splitSelector
* @param selector {String} the selector-item to check the match for
* @return {Array} splitted selectors
* @protected
* @private
* @since 0.0.1
*/
_splitSelector = function(selector) {
var list = [],
len = selector.length,
sel = '',
i, character, insideDataAttr;
for (i=0; i<len; i++) {
character = selector[i];
if (character==='[') {
sel += character;
insideDataAttr = true;
}
else if (character===']') {
sel += character;
insideDataAttr = false;
}
else if (insideDataAttr || !SPLIT_CHARACTER[character]) {
sel += character;
}
else {
// unique selectoritem is found, add it to the list
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
// in case the last character was '>', '+' or '~', we need to add it as a separate item
STORABLE_SPLIT_CHARACTER[character] && (list[list.length]=character);
}
}
// add the last item
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
return list;
};
_batchEmit = function() {
// we will exactly define the 'UI:'-event --> Itags will have another emitterName
MUTATION_EVENTS.each(function (mutationEvents, vnode) {
var domNode = vnode.domNode;
if (mutationEvents[EV_REMOVED]) {
domNode.emit('UI:'+EV_REMOVED);
}
else if (mutationEvents[EV_INSERTED]) {
domNode.emit('UI:'+EV_INSERTED);
}
else {
// contentchange and attributechanges can go hand in hand
mutationEvents.each(function(value, evt) {
domNode.emit('UI:'+evt, (evt===EV_CONTENT_CHANGE) ? null : {changed: value});
});
}
});
MUTATION_EVENTS.clear();
BATCH_WILL_RUN = false;
};
_emitDestroyChildren = function(vnode) {
var children = vnode.vChildren,
len = children.length,
i, vChild;
for (i=0; i<len; i++) {
vChild = children[i];
vChild._emit(EV_REMOVED);
}
};
_markRemoved = function(vnode) {
var vChildNodes = vnode.vChildNodes,
len, i, vChildNode;
if (vnode.nodeType===1) {
Object.protectedProp(vnode, 'removedFromDOM', true);
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && _markRemoved(vChildNode);
}
}
}
};
/**
* A safe way to remove a dom-node, even if it is not present.
* Will not throw a JS error when the "to be removed" node isn't in the dom
*
* @method _tryRemoveDomNode
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param childDomNode {DOMNode} the node that needs to be removed
* @since 0.0.1
*/
_tryRemoveDomNode = function(parentDomNode, childDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && childDomNode.isItag && childDomNode.isItag()) {
DOCUMENT._itagList.remove(childDomNode);
}
try {
parentDomNode._removeChild(childDomNode);
}
catch(err) {}
};
/**
* A safe way to replace a dom-node, even if the "to be replaced"-node it is not present.
* Will not throw a JS error when the "to be replaced" node isn't in the dom. in that case, the new node will
* be appended
*
* @method _tryReplaceChild
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param newChildDomNode {DOMNode} the node that needs to be replaced
* @param oldChildDomNode {DOMNode} the node that needs to be inserted
* @since 0.0.1
*/
_tryReplaceChild = function(parentDomNode, newChildDomNode, oldChildDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && oldChildDomNode.isItag && oldChildDomNode.isItag()) {
DOCUMENT._itagList.remove(oldChildDomNode);
}
try {
parentDomNode._replaceChild(newChildDomNode, oldChildDomNode);
}
catch(err) {
// if the childDomNode isn;t there any more - for whatever reason - the we need to append the newChildNode
parentDomNode._appendChild(newChildDomNode);
}
};
vNodeProto = window._ITSAmodules.VNode = {
/**
* Check whether the vnode's domNode is equal, or contains the specified Element.
*
* @method contains
* @return {Boolean} whether the vnode's domNode is equal, or contains the specified Element.
* @since 0.0.1
*/
contains: function(otherVNode, noProtectedSearch) {
var instance = this;
if (otherVNode && (otherVNode.destroyed || (noProtectedSearch && otherVNode._systemNode))) {
return false;
}
while (otherVNode && (otherVNode!==instance)) {
otherVNode = otherVNode.vParent;
if (otherVNode && noProtectedSearch && (otherVNode._systemNode || (otherVNode.isItag && otherVNode.domNode.contentHidden))) {
return false;
}
}
return (otherVNode===instance);
},
/**
* Empties the vnode.
*
* Syncs with the dom.
*
* @method empty
* @param [full=false] {Boolean} whether system-nodes should be removed as well
* @chainable
* @since 0.0.1
*/
empty: function(full) {
delete this._scripts;
return this._setChildNodes([], null, full);
},
/**
* Returns the first child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method firstOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the first child-vnode or null when not present
* @since 0.0.1
*/
firstOfVChildren: function(cssSelector) {
var instance = this,
found, i, len, vChildren, element;
if (!cssSelector) {
return instance.vFirstElementChild;
}
vChildren = instance.vChildren;
len = vChildren.length;
for (i=0; !found && (i<len); i++) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
return found;
},
/**
* Gets the innerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* Only valid for nodetype=1 (HTMLElements)
*
* @method getHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String|undefined} the innerHTML without the elements specified, or `undefined` when not an HTMLElement
* @since 0.0.1
*/
getHTML: function(exclude, includeSystemNodes) {
var instance = this,
html, vChildNodes, len, i, vChildNode;
if (instance.nodeType===1) {
if (exclude) {
Array.isArray(exclude) || (exclude=[exclude]);
}
html = '';
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
switch (vChildNode.nodeType) {
case 1:
(exclude && exclude.contains(vChildNode.domNode)) || (!includeSystemNodes && vChildNode._systemNode) || (html+=vChildNode.getOuterHTML(exclude, includeSystemNodes));
break;
case 3:
html += vChildNode.text.replace(/</g, '<').replace(/>/g, '>');
break;
case 8:
html += '<!--' + vChildNode.text.replace(/</g, '<').replace(/>/g, '>') + '-->';
}
}
}
return html;
},
/**
* Gets the outerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* @method getOuterHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String} the outerHTML
* @since 0.0.1
*/
getOuterHTML: function(exclude, includeSystemNodes) {
var instance = this,
html,
attrs = instance.attrs;
if (instance.nodeType===1) {
if (instance.nodeType!==1) {
return instance.textContent;
}
html = '<' + instance.tag.toLowerCase();
attrs.each(function(value, key) {
html += ' '+key+'="'+value+'"';
});
instance.isVoid && (html += '/');
html += '>';
if (!instance.isVoid) {
html += ((!includeSystemNodes && instance.isItag) ? '' : instance.getHTML(exclude, includeSystemNodes)) + '</' + instance.tag.toLowerCase() + '>';
}
}
return html;
},
/**
* Checks whether the vnode has any vChildNodes (nodeType of 1, 3 or 8).
*
* @method hasVChildNodes
* @return {Boolean} whether the vnode has any vChildNodes.
* @since 0.0.1
*/
hasVChildNodes: function() {
return this.vChildNodes ? (this.vChildNodes.length>0) : false;
},
/**
* Checks whether the vnode has any vChildren (vChildNodes with nodeType of 1).
*
* @method hasVChildren
* @return {Boolean} whether the vnode has any vChildren.
* @since 0.0.1
*/
hasVChildren: function() {
return this.vChildNodes ? (this.vChildren.length>0) : false;
},
/**
* Checks whether the className is present on the vnode.
*
* @method hasClass
* @param className {String|Array} the className to check for. May be an Array of classNames, which all needs to be present.
* @return {Boolean} whether the className (or classNames) is present on the vnode
* @since 0.0.1
*/
hasClass: function(className) {
var instance = this,
check = function(cl) {
return !!instance.classNames[cl];
};
if (!instance.classNames) {
return false;
}
if (typeof className === STRING) {
return check(className);
}
else if (Array.isArray(className)) {
return className.every(check);
}
return false;
},
/**
* Returns the last child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method lastOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the last child-vnode or null when not present
* @since 0.0.1
*/
lastOfVChildren: function(cssSelector) {
var vChildren = this.vChildren,
found, i, element;
if (vChildren) {
if (!cssSelector) {
return this.vLastElementChild;
}
for (i=vChildren.length-1; !found && (i>=0); i--) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
}
return found;
},
/**
* Checks whether the vnode matches one of the specified selectors. `selectors` can be one, or multiple css-selectors,
* separated by a `comma`. For example: "#myid li.red blue" is one selector, "div.red, div.blue, div.green" are three selectors.
*
* @method matchesSelector
* @param selectors {String} one or more css-selectors
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches one of the selectors
* @since 0.0.1
*/
matchesSelector: function(selectors, relatedVNode) {
var instance = this;
if (instance.nodeType!==1) {
return false;
}
selectors = selectors.split(',');
// we can use Array.some, because there won't be many separated selectoritems,
// so the final invocation won't be delayed much compared to looping
return selectors.some(function(selector) {
return _matchesOneSelector(instance, selector, relatedVNode);
});
},
/**
* Reloads the DOM-attribute into the vnode.
*
* @method matchesSelector
* @param attributeName {String} the name of the attribute to be reloaded.
* @return {Node} the domNode that was reloaded.
* @since 0.0.1
*/
reloadAttr: function(attributeName) {
var instance = this,
domNode = instance.domNode,
attributeValue = domNode._getAttribute(attributeName),
attrs = instance.attrs,
extractStyle, extractClass;
if (instance.nodeType===1) {
attributeValue || (attributeValue='');
if (attributeValue==='') {
delete attrs[attributeName];
// in case of STYLE attributeName --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attributeName --> special treatment
(attributeName===CLASS) && (instance.classNames={});
// in case of ID attributeName --> special treatment
if ((attributeName===ID) && (instance.id)) {
delete nodeids[instance.id];
delete instance.id;
}
}
else {
attrs[attributeName] = attributeValue;
// in case of STYLE attributeName --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(attributeValue);
attributeValue = extractStyle.attrStyle;
if (attributeValue) {
attrs.style = attributeValue;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attributeName --> special treatment
extractClass = extractor.extractClass(attributeValue);
attributeValue = extractClass.attrClass;
if (attributeValue) {
attrs[CLASS] = attributeValue;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (instance.id!==attributeValue) && (delete nodeids[instance.id]);
instance.id = attributeValue;
nodeids[attributeValue] = domNode;
}
}
}
return domNode;
},
/**
* Returns the vnode's style in a serialized form: the way it appears in the dom.
*
* @method serializeStyles
* @return {String} vnode's style
* @since 0.0.1
*/
serializeStyles: function() {
return extractor.serializeStyles(this.styles);
},
/**
* Sets the vnode's and dom-nodes inner HTML.
*
* Syncs with the dom. Can be invoked multiple times without issues.
*
* @method setHTML
* @param content {String} the innerHTML
* @param [suppressItagRender] {Boolean} to suppress Itags from rendering
* @param [allowScripts=false] {Boolean} whether scripts are allowed --> these should be defined with `xscript` instead of `script`
* @chainable
* @since 0.0.1
*/
setHTML: function(content, suppressItagRender, allowScripts) {
var instance = this;
instance._setChildNodes(htmlToVNodes(content, vNodeProto, instance.ns, null, suppressItagRender, allowScripts), suppressItagRender);
return instance;
},
/**
* Syncs the vnode's nodeid (if available) inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom. Can be invoked multiple times without issues.
*
* @method storeId
* @chainable
* @since 0.0.1
*/
storeId: function() {
// store node/vnode inside WeakMap:
var instance = this;
instance.id ? (nodeids[instance.id]=instance.domNode) : (delete nodeids[instance.id]);
return instance;
},
//---- private ------------------------------------------------------------------
_addToTaglist: function() {
var instance = this,
itagList;
if (instance.isItag) {
itagList = DOCUMENT.getItags(); // also reads the dom if the list isn't build yet
instance._data || Object.protectedProp(instance, '_data', {});
if (!instance._data.ce_destroyed && !itagList.contains(instance.domNode)) {
itagList.push(instance.domNode);
}
}
},
/**
* Adds a vnode to the end of the list of vChildNodes.
*
* Syncs with the DOM.
*
* @method _appendChild
* @param VNode {vnode} vnode to append
* @private
* @return {Node} the Node that was appended
* @since 0.0.1
*/
_appendChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
size, noProccessScript, scriptContent;
if ((VNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (VNode.attrs && VNode.attrs.src) ? VNode.attrs.src : VNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
VNode._moveToParent(instance);
instance.domNode._appendChild(domNode);
if (VNode.nodeType===3) {
size = instance.vChildNodes.length;
instance._normalize();
// if the size changed, then the domNode was merged
(size===instance.vChildNodes.length) || (domNode=instance.vChildNodes[instance.vChildNodes.length-1].domNode);
}
if (VNode.nodeType===1) {
VNode._addToTaglist();
VNode._emit(EV_INSERTED);
}
return domNode;
}
},
/**
* Removes the vnode from its parent vChildNodes- and vChildren-list.
*
* Does NOT sync with the dom.
*
* @method _deleteFromParent
* @private
* @chainable
* @since 0.0.1
*/
_deleteFromParent: function() {
var instance = this,
vParent = instance.vParent;
if (vParent && vParent.vChildNodes) {
vParent.vChildNodes.remove(instance);
// force to recalculate the vChildren on a next call:
(instance.nodeType===1) && (vParent._vChildren=null);
}
return instance;
},
/**
* Cleans up (empties) vnode's `_data`
*
* @method _cleanData
* @private
* @chainable
* @since 0.0.1
*/
_cleanData: function() {
var instance = this,
data = instance._data;
data && data.each(
function(value, key) {
delete data[key];
}
);
return instance;
},
/**
* Cleans up (removes) duplicated `style` definitions.
*
* @method _cleanupStyle
* @private
* @chainable
* @since 0.0.1
*/
_cleanupStyle: function() {
var instance = this,
compare = [],
removal = [],
vChildren = instance.vChildren,
len = vChildren.length,
vChild, i, styleContent, vChildInner;
for (i=0; i<len; i++) {
vChild = vChildren[i];
if (vChild.tag==='STYLE') {
vChildInner = vChild.vChildNodes[0];
if (vChildInner) {
styleContent = vChildInner.text;
if (compare.contains(styleContent)) {
removal[removal.length] = vChild;
}
else {
compare[compare.length] = styleContent;
}
}
else {
// empty style-tag
removal[removal.length] = vChild;
}
}
}
removal.forEach(function(vnode) {
instance._removeChild(vnode);
});
},
/**
* Destroys the vnode and all its vnode-vChildNodes.
* Removes it from its vParent.vChildNodes list,
* also removes its definitions inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom.
*
* @method _destroy
* @private
* @chainable
* @since 0.0.1
*/
_destroy: function(silent) {
var instance = this,
vChildNodes = instance.vChildNodes,
len, i, vChildNode, vParent, treeNodes;
if (!instance.destroyed) {
if (!silent) {
// Because we don't want to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
later(function() {
instance._emit(EV_REMOVED, null, null, null, true);
}, 5);
}
Object.protectedProp(instance, 'destroyed', true);
// first: determine the dom-tree, which module `event-dom` needs to determine where the node was before it was destroyed:
treeNodes = [instance];
vParent = instance.vParent;
while (vParent) {
treeNodes[treeNodes.length] = vParent;
vParent = vParent.vParent;
}
// mark all its vChildNodes so we can see if the node is in the DOM
_markRemoved(instance);
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && instance.isItag) {
DOCUMENT._itagList.remove(instance.domNode);
}
instance._scripts && (instance._scripts.length=0);
// The definite cleanup needs to be done after a timeout:
// someone might need to handle the Element when removed (fe to cleanup specific things)
later(function() {
instance._cleanData();
if (instance.nodeType===1) {
// _destroy all its vChildNodes
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && vChildNode._destroy(true);
}
}
}
instance._vChildren = null;
// explicitely set instance.domNode._vnode and instance.domNode to null in order to prevent problems with the GC (we break the circular reference)
delete instance.domNode._vnode;
// if valid id, then _remove the DOMnodeRef from internal hash
instance.id && delete nodeids[instance.id];
}, silent ? 0 : DESTROY_DELAY);
instance._deleteFromParent();
// Do not make domNode `null` --> it could be used even when not in the dom
}
return instance;
},
_emit: function(evt, attribute, newValue, prevValue, destroyEvt) {
/**
* Emitted by every Element that gets inserted.
*
* @event nodeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets removed.
*
* @event noderemove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets its content changed (innerHTML/innerText).
*
* @event nodecontentchange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute inserted.
*
* @event attributeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* </ul>
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute removed.
*
* @event attributeremove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Strings of the attributeNames that are removed
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute changed.
*
* @event attributechange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* <li>prevValue</li>
* </ul>
* @since 0.1
*/
var instance = this,
silent, attrMutations, mutationEvents, mutation, vParent;
if (!DOCUMENT.hasMutationSubs || (instance.nodeType!==1)) {
return;
}
silent = !!DOCUMENT._suppressMutationEvents;
if (!silent && (destroyEvt || !instance.destroyed)) {
// Because we don't wannt to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
mutationEvents = MUTATION_EVENTS.get(instance) || {};
if (attribute) {
attrMutations = mutationEvents[evt] || [];
if (evt===EV_ATTRIBUTE_REMOVED) {
mutation = attribute;
}
else {
mutation = {
attribute: attribute
};
if ((evt===EV_ATTRIBUTE_INSERTED) || (evt===EV_ATTRIBUTE_CHANGED)) {
mutation.newValue = newValue;
}
if ((evt===EV_ATTRIBUTE_CHANGED) && prevValue) {
mutation.prevValue = prevValue;
}
}
attrMutations.push(mutation);
mutationEvents[evt] = attrMutations;
}
else {
mutationEvents[evt] = true;
}
MUTATION_EVENTS.set(instance, mutationEvents);
// now set all parent to have a nodecontentchange:
vParent = instance.vParent;
vParent && vParent._emit(EV_CONTENT_CHANGE);
// in case of removal we need to emit EV_REMOVED for all children right now
// for they will be actually removed silently after a delay of 1 minute
(evt===EV_REMOVED) && _emitDestroyChildren(instance);
if (!BATCH_WILL_RUN) {
BATCH_WILL_RUN = true;
async(function() {
_batchEmit();
});
}
}
return instance;
},
/**
* Inserts `newVNode` before `refVNode`.
*
* Syncs with the DOM.
*
* @method _insertBefore
* @param newVNode {vnode} vnode to insert
* @param refVNode {vnode} The vnode before which newVNode should be inserted.
* @private
* @return {Node} the Node being inserted (equals domNode)
* @since 0.0.1
*/
_insertBefore: function(newVNode, refVNode) {
var instance = this,
domNode = newVNode.domNode,
index = instance.vChildNodes.indexOf(refVNode),
noProccessScript, scriptContent;
if ((newVNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (newVNode.attrs && newVNode.attrs.src) ? newVNode.attrs.src : newVNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
if (index!==-1) {
newVNode._moveToParent(instance, index);
instance.domNode._insertBefore(domNode, refVNode.domNode);
(newVNode.nodeType===3) && instance._normalize();
if (newVNode.nodeType===1) {
newVNode._addToTaglist();
newVNode._emit(EV_INSERTED);
}
return domNode;
}
else {
console.warn('trying to insert before, but no ref-node found. Will append the new node');
return instance._appendChild(newVNode);
}
}
},
/**
* Moves the vnode from its current parent.vChildNodes list towards a new parent vnode at the specified position.
*
* Does NOT sync with the dom.
*
* @method _moveToParent
* @param parentVNode {vnode} the parent-vnode
* @param [index] {Number} the position of the child. When not specified, it will be appended.
* @private
* @chainable
* @since 0.0.1
*/
_moveToParent: function(parentVNode, index) {
var instance = this,
vParent = instance.vParent;
instance._deleteFromParent();
instance.vParent = parentVNode;
parentVNode.vChildNodes || (parentVNode.vChildNodes=[]);
(typeof index==='number') ? parentVNode.vChildNodes.insertAt(instance, index) : (parentVNode.vChildNodes[parentVNode.vChildNodes.length]=instance);
// force to recalculate the vChildren on a next call:
vParent && (instance.nodeType===1) && (vParent._vChildren = null);
// force to recalculate the vChildren on a next call:
parentVNode && (instance.nodeType===1) && (parentVNode._vChildren=null);
return instance;
},
/**
* Removes empty TextNodes and merges following TextNodes inside the vnode.
*
* Syncs with the dom.
*
* @method _normalize
* @private
* @chainable
* @since 0.0.1
*/
_normalize: function() {
var instance = this,
domNode = instance.domNode,
vChildNodes = instance.vChildNodes,
changed = false,
i, preChildNode, vChildNode;
if (!instance._unNormalizable && vChildNodes) {
for (i=vChildNodes.length-1; i>=0; i--) {
vChildNode = vChildNodes[i];
preChildNode = vChildNodes[i-1]; // i will get the value `-1` eventually, which leads into undefined preChildNode
if (vChildNode.nodeType===3) {
if (vChildNode.text==='') {
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
else if (preChildNode && preChildNode.nodeType===3) {
preChildNode.text += vChildNode.text;
preChildNode.domNode.nodeValue = unescapeEntities(preChildNode.text);
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
}
}
}
changed && instance._emit(EV_CONTENT_CHANGE);
return instance;
},
/**
* Makes the vnode `normalizable`. Could be set to `false` when batch-inserting nodes, while `normalizaing` manually at the end.
* Afterwards, you should always reset `normalizable` to true.
*
* @method _normalizable
* @param value {Boolean} whether the vnode should be normalisable.
* @private
* @chainable
* @since 0.0.1
*/
_normalizable: function(value) {
var instance = this;
value ? (delete instance._unNormalizable) : (instance._unNormalizable=true);
return instance;
},
/**
* Prevents MutationObserver from making the dom sync with the vnode.
* Should be used when manipulating the dom from within the vnode itself (to preventing looping)
*
* @method _noSync
* @chainable
* @private
* @since 0.0.1
*/
_noSync: function() {
var instance = this;
if (!instance._nosync) {
instance._nosync = true;
async(function() {
instance._nosync = false;
});
}
return instance;
},
/**
* Removes the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _removeAttr
* @param attributeName {String}
* @private
* @chainable
* @since 0.0.1
*/
_removeAttr: function(attributeName, suppressItagRender) {
var instance = this,
attributeNameSplitted, ns;
if (instance.isItag && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
console.warn('Not allowed to remove the attribute '+attributeName);
return instance;
}
if (instance.attrs[attributeName]!==undefined) {
delete instance.attrs[attributeName];
// in case of STYLE attribute --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attribute --> special treatment
(attributeName===CLASS) && (instance.classNames={});
if (attributeName===ID) {
delete nodeids[instance.id];
delete instance.id;
}
instance._emit(EV_ATTRIBUTE_REMOVED, attributeName);
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
instance.domNode._removeAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName);
}
else {
instance.domNode._removeAttribute(attributeName);
}
}
return instance;
},
/**
* Removes the vnode's child-vnode from its vChildren and the DOM.
*
* Syncs with the DOM.
*
* @method removeChild
* @param VNode {vnode} the child-vnode to remove
* @private
* @since 0.0.1
*/
_removeChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
hadFocus = domNode && domNode.hasFocus && domNode.hasFocus() && (VNode.attrs['fm-lastitem']==='true'),
parentVNode = VNode.vParent;
VNode._destroy();
_tryRemoveDomNode(instance.domNode, VNode.domNode);
instance._normalize();
// now, reset the focus on focusmanager when needed:
if (hadFocus) {
while (parentVNode && !parentVNode.attrs['fm-manage']) {
parentVNode = parentVNode.vParent;
}
parentVNode && parentVNode.domNode.focus();
}
},
/**
* Replaces the current vnode at the parent.vChildNode list by `newVNode`
*
* Does NOT sync with the dom.
*
* @method _replaceAtParent
* @param newVNode {Object} the new vnode which should take over the place of the current vnode
* @private
* @chainable
* @since 0.0.1
*/
_replaceAtParent: function(newVNode) {
var instance = this,
vParent = instance.vParent,
vChildNodes, index;
if (vParent && (vChildNodes=vParent.vChildNodes)) {
index = vChildNodes.indexOf(instance);
// force to recalculate the vChildren on a next call:
((instance.nodeType===1) || (newVNode.nodeType===1)) && (instance.vParent._vChildren=null);
vChildNodes[index] = newVNode;
}
return instance._destroy();
},
/**
* Sets the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _setAttr
* @param attributeName {String}
* @param value {String} the value for the attributeName
* @param [force=false] {Boolean} force the attribute to be set, even if restrictions would deny it
* @private
* @chainable
* @since 0.0.1
*/
_setAttr: function(attributeName, value, force, suppressItagRender) {
var instance = this,
extractStyle, extractClass,
attrs = instance.attrs,
prevVal = attrs[attributeName],
domNode = instance.domNode,
attributeNameSplitted, ns;
if (instance.isItag && !force && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
if (prevVal!=value) {
console.warn('Not allowed to set the attribute '+attributeName);
}
return instance;
}
// don't check by !== --> value isn't parsed into a String yet
if (prevVal && ((value===undefined) || (value===null))) {
instance._removeAttr(attributeName, suppressItagRender);
return instance;
}
// attribute-values are always Strings:
value = String(value);
// attribute-values will be stored without " or '
value = value.replace(/"/g, '"').replace(/'/g, "'");
if (prevVal!=value) {
attrs[attributeName] = value;
// in case of STYLE attribute --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(value);
value = extractStyle.attrStyle;
if (value) {
attrs.style = value;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attribute --> special treatment
extractClass = extractor.extractClass(value);
value = extractClass.attrClass;
if (value) {
attrs[CLASS] = value;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (delete nodeids[instance.id]);
instance.id = value;
nodeids[value] = domNode;
}
instance._emit(prevVal ? EV_ATTRIBUTE_CHANGED : EV_ATTRIBUTE_INSERTED, attributeName, value, prevVal);
// when set in the dom --> quotes need to be set as "
value = value.replace(/"/g, '"');
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
domNode._setAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName, value);
}
else {
domNode._setAttribute(attributeName, value);
}
}
return instance;
},
/**
* Redefines the attributes of both the vnode as well as its related dom-node. The new
* definition replaces any previous attributes (without touching unmodified attributes).
* the `is` attribute cannot be changed for itags.
*
* Syncs the new vnode's attributes with the dom.
*
* @method _setAttrs
* @param newAttrs {Object|Array} the new attributes to be set
* @private
* @chainable
* @since 0.0.1
*/
_setAttrs: function(newAttrs, suppressItagRender) {
// does sync the DOM
var instance = this,
attrsObj, attr, attrs, i, key, keys, len, value;
if (instance.nodeType!==1) {
return;
}
instance._noSync();
attrs = instance.attrs;
attrs.id && (delete nodeids[attrs.id]);
if (Object.isObject(newAttrs)) {
attrsObj = newAttrs;
}
else {
attrsObj = {};
len = newAttrs.length;
for (i=0; i<len; i++) {
attr = newAttrs[i];
attrsObj[attr.name] = attr.value;
}
}
if (instance.isItag && !suppressItagRender) {
if (attrs.is) {
attrsObj.is = attrs.is;
}
else {
delete attrsObj.is;
delete attrsObj.Is;
delete attrsObj.iS;
delete attrsObj.IS;
}
}
// first _remove the attributes that are no longer needed.
// quickest way for object iteration: http://jsperf.com/object-keys-iteration/20
keys = Object.keys(attrs);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
attrsObj[key] || instance._removeAttr(key, suppressItagRender);
}
// next: every attribute that differs: redefine
keys = Object.keys(attrsObj);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
value = attrsObj[key];
(attrs[key]===value) || instance._setAttr(key, value, suppressItagRender);
}
return instance;
},
/**
* Redefines the childNodes of both the vnode as well as its related dom-node. The new
* definition replaces any previous nodes. (without touching unmodified nodes).
*
* Syncs the new vnode's childNodes with the dom.
*
* @method _setChildNodes
* @param newVChildNodes {Array} array with vnodes which represent the new childNodes
* @private
* @chainable
* @since 0.0.1
*/
_setChildNodes: function(newVChildNodes, suppressItagRender, removeSystemElements) {
// does sync the DOM
var instance = this,
vChildNodes = instance.vChildNodes || [],
domNode = instance.domNode,
forRemoval = [],
i, oldChild, newChild, newLength, len, len2, childDomNode, nodeswitch, bkpAttrs, cleanupStyle,
process, bkpChildNodes, needNormalize, prevSuppress, scriptContent, _scripts, scriptLen, j;
instance._noSync();
// first: reset ._vChildren --> by making it empty, its getter will refresh its list on a next call
instance._vChildren = null;
// if newVChildNodes is undefined, then we assume it to be empty --> an empty array
newVChildNodes || (newVChildNodes=[]);
// quickest way to loop through array is by using for loops: http://jsperf.com/array-foreach-vs-for-loop/5
len = vChildNodes.length;
// we need to add the systemNodes -if any- to the new newVChildNodes: they should be retained.
// SystemNodes are always places at the beginning, so they won't get reshuffled:
if (!removeSystemElements) {
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
if (oldChild._systemNode) {
newVChildNodes.insertAt(oldChild, i);
}
else {
break;
}
}
}
newLength = newVChildNodes.length;
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
childDomNode = oldChild.domNode;
if (i < newLength) {
newChild = newVChildNodes[i];
newChild.vParent || (newChild.vParent=instance);
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
/*jshint boss:true */
switch (nodeswitch=NODESWITCH[oldChild.nodeType][newChild.nodeType]) {
/*jshint boss:false */
case 1: // oldNodeType==Element, newNodeType==Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
if ((oldChild.tag!==newChild.tag) ||
((oldChild.tag===newChild.tag) && oldChild.isItag && (oldChild.attrs.is!==newChild.attrs.is))) {
// new tag --> completely replace
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
/*jshint proto:true */
oldChild.isItag && oldChild.domNode.destroyUI && oldChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
newChild.attrs = {}; // reset to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
oldChild._replaceAtParent(newChild);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
else {
// same tag --> only update what is needed
// NOTE: when this._unchangableAttrs exists, an itag-element syncs its UI -->
if (oldChild._data) {
// we might need to set the class `itag-rendered` when the attributeData says so:
// this happens when an itag gets refreshed with an unrendered definition
if (oldChild._data.itagRendered && !newChild.hasClass('itag-rendered')) {
newChild.classNames['itag-rendered'] = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' '+'itag-rendered';
}
else {
newChild.attrs[CLASS] = 'itag-rendered';
}
}
// we might need to set the class `focussed` when the attributeData says so:
// this happens when an itag gets rerendered: its renderFn doesn't know if any elements
// were focussed
if (oldChild._data.focussed && !newChild.hasClass('focussed')) {
newChild.classNames.focussed = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' ' + 'focussed';
}
else {
newChild.attrs[CLASS] = 'focussed';
}
}
if (oldChild._data['fm-tabindex']) {
// node has the tabindex set by the focusmanager,
// but that info might got lost with re-rendering of the new element
newChild.attrs.tabindex = '0';
}
}
if (oldChild.isItag) {
prevSuppress = DOCUMENT._suppressMutationEvents || false;
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
/*jshint proto:true */
newChild.domNode.destroyUI && newChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
oldChild._setAttrs(newChild.attrs, suppressItagRender);
newChild._destroy(true); // destroy through the vnode and removing from DOCUMENT._itagList
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(prevSuppress);
}
else {
oldChild._setAttrs(newChild.attrs, suppressItagRender);
// next: sync the vChildNodes:
oldChild._setChildNodes(newChild.vChildNodes, suppressItagRender);
}
// reset ref. to the domNode, for it might have been changed by newChild:
oldChild.id && (nodeids[oldChild.id]=childDomNode);
newVChildNodes[i] = oldChild;
}
}
break;
case 2: // oldNodeType==Element, newNodeType==TextNode
// case2 and case3 should be treated the same
case 3: // oldNodeType==Element, newNodeType==Comment
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
oldChild._replaceAtParent(newChild);
instance._emit(EV_CONTENT_CHANGE);
break;
case 4: // oldNodeType==TextNode, newNodeType==Element
// case4 and case7 should be treated the same
case 7: // oldNodeType==Comment, newNodeType==Element
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
// oldChild.isVoid = newChild.isVoid;
// delete oldChild.text;
instance._emit(EV_CONTENT_CHANGE);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
break;
case 5: // oldNodeType==TextNode, newNodeType==TextNode
// case5 and case9 should be treated the same
case 9: // oldNodeType==Comment, newNodeType==Comment
if (oldChild.text!==newChild.text) {
oldChild.text = newChild.text;
oldChild.domNode.nodeValue = unescapeEntities(newChild.text);
instance._emit(EV_CONTENT_CHANGE);
}
newVChildNodes[i] = oldChild;
break;
case 6: // oldNodeType==TextNode, newNodeType==Comment
// case6 and case8 should be treated the same
case 8: // oldNodeType==Comment, newNodeType==TextNode
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = oldChild.vParent;
instance._emit(EV_CONTENT_CHANGE);
}
if ((nodeswitch===2) || (nodeswitch===5) || (nodeswitch===8)) {
needNormalize = true;
}
}
else {
// _remove previous definition
_tryRemoveDomNode(domNode, oldChild.domNode);
// the oldChild needs to be removed, however, this cannot be done right now, for it would effect the loop
// so we store it inside a hash to remove it later
forRemoval[forRemoval.length] = oldChild;
}
}
// now definitely remove marked childNodes:
len2 = forRemoval.length;
for (i=0; i<len2; i++) {
forRemoval[i]._destroy();
}
// now we add all new vChildNodes that go beyond `len`:
for (i = len; i < newLength; i++) {
newChild = newVChildNodes[i];
newChild.vParent = instance;
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
switch (newChild.nodeType) {
case 1: // Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
domNode._appendChild(newChild.domNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
break;
case 3: // TextNode
needNormalize = true;
// we need to break through --> no `break`
/* falls through */
default: // TextNode or CommentNode
// newChild.domNode.nodeValue = newChild.text;
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
domNode._appendChild(newChild.domNode);
instance._emit(EV_CONTENT_CHANGE);
}
newChild.storeId();
}
instance.vChildNodes = newVChildNodes;
needNormalize && instance._normalize();
cleanupStyle && instance._cleanupStyle();
return instance;
},
_setUnchangableAttrs: function(unchangableObj) {
this._unchangableAttrs = unchangableObj;
}
};
//---- properties ------------------------------------------------------------------
/**
* A hash of all the `attributes` of the vnode's representing dom-node.
*
* @property attrs
* @type Object
* @since 0.0.1
*/
/**
* Hash with all the classes of the vnode. Every class represents a key, all values are set `true`.
*
* @property classNames
* @type Object
* @since 0.0.1
*/
/**
* The `id` of the vnode's representing dom-node (if any).
*
* @property id
* @type String
* @since 0.0.1
*/
/**
* Tells whether tag is a void Element. Examples are: `br`, `img` and `input`. Non-void Elements are f.e. `div` and `table`.
* For TextNodes and CommentNodes, this property is `undefined`.
*
* @property isVoid
* @type Boolean
* @since 0.0.1
*/
/**
* The `nodeType` of the vnode's representing dom-node (1===ElementNode, 3===TextNode, 8===CommentNode).
*
* @property nodeType
* @type Number
* @since 0.0.1
*/
/**
* The `tag` of the vnode's representing dom-node (allways uppercase).
*
* @property tag
* @type String
* @since 0.0.1
*/
/**
* The `content` of the vnode's representing dom-node, in case it is a TextNode or CommentNode.
* Equals dom-node.nodeValue.
*
* Is `undefined` for ElementNodes.
*
* @property text
* @type String
* @since 0.0.1
*/
/**
* Hash with all the childNodes (vnodes). vChildNodes are any kind of vnodes (nodeType===1, 3 or 8)
*
* @property vChildNodes
* @type Array
* @since 0.0.1
*/
/**
* The underlying `dom-node` that the vnode represents.
*
* @property domNode
* @type domNode
* @since 0.0.1
*/
/**
* vnode's parentNode (defined as a vnode itself).
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
Object.defineProperties(vNodeProto, {
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property innerHTML
* @type String
* @since 0.0.1
*/
innerHTML: {
get: function() {
return this.getHTML();
},
set: function(v) {
this.setHTML(v);
}
},
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property nodeValue
* @type String
* @since 0.0.1
*/
nodeValue: {
get: function() {
var instance = this;
return ((instance.nodeType===3) || (instance.nodeType===8)) ? instance.text : null;
},
set: function(v) {
var instance = this,
newTextContent, prevTextContent;
if ((instance.nodeType===3) || (instance.nodeType===8)) {
prevTextContent = instance.domNode.textContent;
instance.domNode.textContent = v;
// set .text AFTER the dom-node is updated --> the content might be escaped!
newTextContent = instance.text = instance.domNode.textContent;
(newTextContent!==prevTextContent) && instance._emit(EV_CONTENT_CHANGE);
}
}
},
/**
* Gets or sets the outerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property outerHTML
* @type String
* @since 0.0.1
*/
outerHTML: {
get: function() {
return this.getOuterHTML();
},
set: function(v) {
var instance = this,
vParent = instance.vParent,
id = instance.attrs.id,
vnode, vnodes, bkpAttrs, bkpChildNodes, i, len, vChildNodes, isLastChildNode, index, refDomNode;
if ((instance.nodeType!==1) || !vParent) {
return;
}
instance._noSync();
vChildNodes = vParent.vChildNodes;
index = vChildNodes.indexOf(instance);
isLastChildNode = (index===(vChildNodes.length-1));
isLastChildNode || (refDomNode=vChildNodes[index+1].domNode);
vnodes = htmlToVNodes(v, vNodeProto, vParent.ns, vParent);
len = vnodes.length;
if (len>0) {
// the first vnode will replace the current instance:
vnode = vnodes[0];
if (vnode.nodeType===1) {
if (vnode.tag!==instance.tag) {
// new tag --> completely replace
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
id && (delete nodeids[id]);
vnode.attrs = {}; // reset to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
// vnode.attrs = bkpAttrs;
// vnode.vChildNodes = bkpChildNodes;
vnode.id && (nodeids[vnode.id]=vnode.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
else {
instance._setAttrs(vnode.attrs);
instance._setChildNodes(vnode.vChildNodes);
}
}
else {
id && (delete nodeids[id]);
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
}
for (i=1; i<len; i++) {
vnode = vnodes[i];
switch (vnode.nodeType) {
case 1: // Element
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
vnode.attrs = {}; // reset, to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._insertBefore(vnode.domNode, refDomNode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
break;
default: // TextNode or CommentNode
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._appendChild(vnode.domNode, refDomNode);
}
vnode.storeId();
vnode._moveToParent(vParent, index+i);
}
}
},
/**
* Gets or sets the innerContent of the Node as plain text.
*
* The setter syncs with the DOM.
*
* @property textContent
* @type String
* @since 0.0.1
*/
textContent: {
get: function() {
var instance = this,
text = '',
vChildNodes = instance.vChildNodes,
len, i, vChildNode;
if (instance.nodeType===1) {
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
text += (vChildNode.nodeType===3) ? vChildNode.text : ((vChildNode.nodeType===1) ? vChildNode.textContent : '');
}
}
else {
text = instance.text;
}
return text;
},
set: function(v) {
var vnode = Object.create(vNodeProto);
vnode.domNode = DOCUMENT.createTextNode(v);
// create circular reference:
vnode.domNode._vnode = vnode;
vnode.nodeType = 3;
vnode.text = vnode.domNode.textContent;
this._setChildNodes([vnode]);
}
},
/**
* Hash with all the children (vnodes). vChildren are vnodes that have a representing dom-node that is an HtmlElement (nodeType===1)
*
* @property vChildren
* @type Array
* @since 0.0.1
*/
vChildren: {
get: function() {
var instance = this,
children = instance._vChildren,
vChildNode, vChildNodes, i, len;
vChildNodes = instance.vChildNodes;
if (vChildNodes && !children) {
children = instance._vChildren = [];
len = vChildNodes.length;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
(vChildNode.nodeType===1) && (children[children.length]=vChildNode);
}
}
children || (children = instance._vChildren = []);
return children;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirst
* @type vnode
* @since 0.0.1
*/
vFirst: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstChild;
}
},
/**
* Reference to the first vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirstChild
* @type vnode
* @since 0.0.1
*/
vFirstChild: {
get: function() {
return (this.vChildNodes && this.vChildNodes[0]) || null;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vFirstElement
* @type vnode
* @since 0.0.1
*/
vFirstElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstElementChild;
}
},
/**
* Reference to the first vChild, where the related dom-node an Element (nodeType===1).
*
* @property vFirstElementChild
* @type vnode
* @since 0.0.1
*/
vFirstElementChild: {
get: function() {
return this.vChildren[0] || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLast
* @type vnode
* @since 0.0.1
*/
vLast: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastChild;
}
},
/**
* Reference to the last vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLastChild
* @type vnode
* @since 0.0.1
*/
vLastChild: {
get: function() {
var vChildNodes = this.vChildNodes;
return (vChildNodes && vChildNodes[vChildNodes.length-1]) || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vLastElement
* @type vnode
* @since 0.0.1
*/
vLastElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastElementChild;
}
},
/**
* Reference to the last vChild, where the related dom-node an Element (nodeType===1).
*
* @property vLastElementChild
* @type vnode
* @since 0.0.1
*/
vLastElementChild: {
get: function() {
var vChildren = this.vChildren;
return vChildren[vChildren.length-1] || null;
}
},
/**
* the Parent vnode
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
/**
* Reference to the next of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vNext
* @type vnode
* @since 0.0.1
*/
vNext: {
get: function() {
return _findNodeSibling(this, true);
}
},
/**
* Reference to the next of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vNextElement
* @type vnode
* @since 0.0.1
*/
vNextElement: {
get: function() {
return _findElementSibling(this, true);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vPrevious
* @type vnode
* @since 0.0.1
*/
vPrevious: {
get: function() {
return _findNodeSibling(this);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vPreviousElement
* @type vnode
* @since 0.0.1
*/
vPreviousElement: {
get: function() {
return _findElementSibling(this);
}
}
});
return vNodeProto;
};
},{"./attribute-extractor.js":4441,"./html-parser.js":4445,"./vdom-ns.js":4447,"js-ext/extra/hashmap.js":4399,"js-ext/extra/lightmap.js":4400,"js-ext/lib/array.js":4401,"js-ext/lib/object.js":4402,"js-ext/lib/string.js":4404,"polyfill":4421,"utils/lib/timers.js":4424}],4449:[function(require,module,exports){
arguments[4][100][0].apply(exports,arguments)
},{"./partials/extend-document.js":4443,"./partials/extend-element.js":4444,"./partials/node-parser.js":4446,"js-ext/extra/hashmap.js":4399,"js-ext/lib/object.js":4402,"utils/lib/timers.js":4424}],4450:[function(require,module,exports){
module.exports=require(14)
},{}],4451:[function(require,module,exports){
module.exports=require(15)
},{}],4452:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4450,"./lib/window.console.js":4451}],4453:[function(require,module,exports){
module.exports=require(1234)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],4454:[function(require,module,exports){
module.exports=require(265)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],4455:[function(require,module,exports){
module.exports=require(266)
},{"./css/drag.css":4454,"event-dom":4456,"js-ext":4552,"js-ext/extra/hashmap.js":4551,"node-plugin":4568,"polyfill":4740,"useragent":4756,"vdom":4809,"window-ext":4810}],4456:[function(require,module,exports){
module.exports=require(267)
},{"event":4460,"js-ext/extra/hashmap.js":4476,"js-ext/lib/array.js":4477,"js-ext/lib/object.js":4478,"js-ext/lib/string.js":4479,"polyfill/polyfill-base.js":4491,"utils":4492,"vdom":4550}],4457:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":4462,"js-ext/lib/object.js":4463,"polyfill/polyfill-base.js":4475}],4458:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":4457}],4459:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":4457,"js-ext/extra/classes.js":4461,"js-ext/lib/object.js":4463}],4460:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":4457,"./event-emitter.js":4458,"./event-listener.js":4459}],4461:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":4463,"js-ext/extra/hashmap.js":4462,"polyfill/polyfill-base.js":4466}],4462:[function(require,module,exports){
module.exports=require(4)
},{}],4463:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4462,"polyfill/polyfill-base.js":4466,"utils":4467}],4464:[function(require,module,exports){
module.exports=require(14)
},{}],4465:[function(require,module,exports){
module.exports=require(15)
},{}],4466:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4464,"./lib/window.console.js":4465}],4467:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4468,"./lib/timers.js":4469}],4468:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4462,"polyfill/polyfill-base.js":4472}],4469:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4472}],4470:[function(require,module,exports){
module.exports=require(14)
},{}],4471:[function(require,module,exports){
module.exports=require(15)
},{}],4472:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4470,"./lib/window.console.js":4471}],4473:[function(require,module,exports){
module.exports=require(14)
},{}],4474:[function(require,module,exports){
module.exports=require(15)
},{}],4475:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4473,"./lib/window.console.js":4474}],4476:[function(require,module,exports){
module.exports=require(4)
},{}],4477:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":4482}],4478:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4476,"polyfill/polyfill-base.js":4482,"utils":4483}],4479:[function(require,module,exports){
module.exports=require(29)
},{}],4480:[function(require,module,exports){
module.exports=require(14)
},{}],4481:[function(require,module,exports){
module.exports=require(15)
},{}],4482:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4480,"./lib/window.console.js":4481}],4483:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4484,"./lib/timers.js":4485}],4484:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4476,"polyfill/polyfill-base.js":4488}],4485:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4488}],4486:[function(require,module,exports){
module.exports=require(14)
},{}],4487:[function(require,module,exports){
module.exports=require(15)
},{}],4488:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4486,"./lib/window.console.js":4487}],4489:[function(require,module,exports){
module.exports=require(14)
},{}],4490:[function(require,module,exports){
module.exports=require(15)
},{}],4491:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4489,"./lib/window.console.js":4490}],4492:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4493,"./lib/timers.js":4494}],4493:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4476,"polyfill/polyfill-base.js":4497}],4494:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4497}],4495:[function(require,module,exports){
module.exports=require(14)
},{}],4496:[function(require,module,exports){
module.exports=require(15)
},{}],4497:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4495,"./lib/window.console.js":4496}],4498:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],4499:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":4503,"js-ext/extra/hashmap.js":4500,"polyfill/polyfill-base.js":4509}],4500:[function(require,module,exports){
module.exports=require(4)
},{}],4501:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":4502,"../lib/object.js":4503,"./classes.js":4499,"js-ext/extra/hashmap.js":4500,"polyfill/lib/weakmap.js":4507}],4502:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":4509}],4503:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4500,"polyfill/polyfill-base.js":4509,"utils":4510}],4504:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":4509}],4505:[function(require,module,exports){
module.exports=require(29)
},{}],4506:[function(require,module,exports){
module.exports=require(14)
},{}],4507:[function(require,module,exports){
module.exports=require(57)
},{}],4508:[function(require,module,exports){
module.exports=require(15)
},{}],4509:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4506,"./lib/window.console.js":4508}],4510:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4511,"./lib/timers.js":4512}],4511:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4500,"polyfill/polyfill-base.js":4515}],4512:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4515}],4513:[function(require,module,exports){
module.exports=require(14)
},{}],4514:[function(require,module,exports){
module.exports=require(15)
},{}],4515:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4513,"./lib/window.console.js":4514}],4516:[function(require,module,exports){
module.exports=require(66)
},{}],4517:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":4516}],4518:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":4516}],4519:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":4516}],4520:[function(require,module,exports){
module.exports=require(14)
},{}],4521:[function(require,module,exports){
module.exports=require(15)
},{}],4522:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4520,"./lib/window.console.js":4521}],4523:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4524,"./lib/timers.js":4525}],4524:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4500,"polyfill/polyfill-base.js":4528}],4525:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4528}],4526:[function(require,module,exports){
module.exports=require(14)
},{}],4527:[function(require,module,exports){
module.exports=require(15)
},{}],4528:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4526,"./lib/window.console.js":4527}],4529:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":4530}],4530:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":4531,"js-ext/lib/object.js":4532}],4531:[function(require,module,exports){
module.exports=require(4)
},{}],4532:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4531,"polyfill/polyfill-base.js":4535,"utils":4536}],4533:[function(require,module,exports){
module.exports=require(14)
},{}],4534:[function(require,module,exports){
module.exports=require(15)
},{}],4535:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4533,"./lib/window.console.js":4534}],4536:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4537,"./lib/timers.js":4538}],4537:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4531,"polyfill/polyfill-base.js":4541}],4538:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4541}],4539:[function(require,module,exports){
module.exports=require(14)
},{}],4540:[function(require,module,exports){
module.exports=require(15)
},{}],4541:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4539,"./lib/window.console.js":4540}],4542:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":4500,"js-ext/lib/object.js":4503,"js-ext/lib/string.js":4505,"polyfill":4522,"polyfill/extra/transition.js":4517,"polyfill/extra/vendorCSS.js":4519}],4543:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":4500,"js-ext/lib/object.js":4503,"polyfill":4522}],4544:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":4500,"js-ext/lib/object.js":4503,"js-ext/lib/string.js":4505,"polyfill":4522}],4545:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":4498,"./attribute-extractor.js":4542,"./element-array.js":4543,"./html-parser.js":4546,"./node-parser.js":4547,"./vdom-ns.js":4548,"./vnode.js":4549,"js-ext/extra/hashmap.js":4500,"js-ext/lib/object.js":4503,"js-ext/lib/promise.js":4504,"js-ext/lib/string.js":4505,"polyfill":4522,"polyfill/extra/transition.js":4517,"polyfill/extra/transitionend.js":4518,"polyfill/extra/vendorCSS.js":4519,"utils":4523,"window-ext":4529}],4546:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":4542,"./vdom-ns.js":4548,"js-ext/extra/hashmap.js":4500,"js-ext/lib/object.js":4503,"polyfill":4522}],4547:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":4542,"./vdom-ns.js":4548,"./vnode.js":4549,"js-ext/extra/hashmap.js":4500,"js-ext/lib/object.js":4503,"polyfill":4522}],4548:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":4500,"js-ext/lib/object.js":4503,"polyfill":4522}],4549:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":4542,"./html-parser.js":4546,"./vdom-ns.js":4548,"js-ext/extra/hashmap.js":4500,"js-ext/extra/lightmap.js":4501,"js-ext/lib/array.js":4502,"js-ext/lib/object.js":4503,"js-ext/lib/string.js":4505,"polyfill":4522,"utils/lib/timers.js":4525}],4550:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":4544,"./partials/extend-element.js":4545,"./partials/node-parser.js":4547,"js-ext/extra/hashmap.js":4500,"js-ext/lib/object.js":4503,"utils/lib/timers.js":4525}],4551:[function(require,module,exports){
module.exports=require(4)
},{}],4552:[function(require,module,exports){
module.exports=require(202)
},{"./lib/array.js":4553,"./lib/function.js":4554,"./lib/json.js":4555,"./lib/object.js":4556,"./lib/promise.js":4557,"./lib/string.js":4558}],4553:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":4561}],4554:[function(require,module,exports){
module.exports=require(204)
},{"polyfill/polyfill-base.js":4561}],4555:[function(require,module,exports){
module.exports=require(205)
},{"polyfill/polyfill-base.js":4561}],4556:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4551,"polyfill/polyfill-base.js":4561,"utils":4562}],4557:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":4561}],4558:[function(require,module,exports){
module.exports=require(29)
},{}],4559:[function(require,module,exports){
module.exports=require(14)
},{}],4560:[function(require,module,exports){
module.exports=require(15)
},{}],4561:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4559,"./lib/window.console.js":4560}],4562:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4563,"./lib/timers.js":4564}],4563:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4551,"polyfill/polyfill-base.js":4567}],4564:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4567}],4565:[function(require,module,exports){
module.exports=require(14)
},{}],4566:[function(require,module,exports){
module.exports=require(15)
},{}],4567:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4565,"./lib/window.console.js":4566}],4568:[function(require,module,exports){
arguments[4][5][0].apply(exports,arguments)
},{"event-dom":4569,"js-ext/extra/classes.js":4664,"js-ext/extra/hashmap.js":4665,"js-ext/lib/object.js":4666,"js-ext/lib/promise.js":4667,"js-ext/lib/string.js":4668,"polyfill":4680,"utils/lib/timers.js":4681,"vdom":4737}],4569:[function(require,module,exports){
module.exports=require(267)
},{"event":4573,"js-ext/extra/hashmap.js":4589,"js-ext/lib/array.js":4590,"js-ext/lib/object.js":4591,"js-ext/lib/string.js":4592,"polyfill/polyfill-base.js":4604,"utils":4605,"vdom":4663}],4570:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":4575,"js-ext/lib/object.js":4576,"polyfill/polyfill-base.js":4588}],4571:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":4570}],4572:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":4570,"js-ext/extra/classes.js":4574,"js-ext/lib/object.js":4576}],4573:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":4570,"./event-emitter.js":4571,"./event-listener.js":4572}],4574:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":4576,"js-ext/extra/hashmap.js":4575,"polyfill/polyfill-base.js":4579}],4575:[function(require,module,exports){
module.exports=require(4)
},{}],4576:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4575,"polyfill/polyfill-base.js":4579,"utils":4580}],4577:[function(require,module,exports){
module.exports=require(14)
},{}],4578:[function(require,module,exports){
module.exports=require(15)
},{}],4579:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4577,"./lib/window.console.js":4578}],4580:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4581,"./lib/timers.js":4582}],4581:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4575,"polyfill/polyfill-base.js":4585}],4582:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4585}],4583:[function(require,module,exports){
module.exports=require(14)
},{}],4584:[function(require,module,exports){
module.exports=require(15)
},{}],4585:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4583,"./lib/window.console.js":4584}],4586:[function(require,module,exports){
module.exports=require(14)
},{}],4587:[function(require,module,exports){
module.exports=require(15)
},{}],4588:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4586,"./lib/window.console.js":4587}],4589:[function(require,module,exports){
module.exports=require(4)
},{}],4590:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":4595}],4591:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4589,"polyfill/polyfill-base.js":4595,"utils":4596}],4592:[function(require,module,exports){
module.exports=require(29)
},{}],4593:[function(require,module,exports){
module.exports=require(14)
},{}],4594:[function(require,module,exports){
module.exports=require(15)
},{}],4595:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4593,"./lib/window.console.js":4594}],4596:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4597,"./lib/timers.js":4598}],4597:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4589,"polyfill/polyfill-base.js":4601}],4598:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4601}],4599:[function(require,module,exports){
module.exports=require(14)
},{}],4600:[function(require,module,exports){
module.exports=require(15)
},{}],4601:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4599,"./lib/window.console.js":4600}],4602:[function(require,module,exports){
module.exports=require(14)
},{}],4603:[function(require,module,exports){
module.exports=require(15)
},{}],4604:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4602,"./lib/window.console.js":4603}],4605:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4606,"./lib/timers.js":4607}],4606:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4589,"polyfill/polyfill-base.js":4610}],4607:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4610}],4608:[function(require,module,exports){
module.exports=require(14)
},{}],4609:[function(require,module,exports){
module.exports=require(15)
},{}],4610:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4608,"./lib/window.console.js":4609}],4611:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],4612:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":4616,"js-ext/extra/hashmap.js":4613,"polyfill/polyfill-base.js":4622}],4613:[function(require,module,exports){
module.exports=require(4)
},{}],4614:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":4615,"../lib/object.js":4616,"./classes.js":4612,"js-ext/extra/hashmap.js":4613,"polyfill/lib/weakmap.js":4620}],4615:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":4622}],4616:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4613,"polyfill/polyfill-base.js":4622,"utils":4623}],4617:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":4622}],4618:[function(require,module,exports){
module.exports=require(29)
},{}],4619:[function(require,module,exports){
module.exports=require(14)
},{}],4620:[function(require,module,exports){
module.exports=require(57)
},{}],4621:[function(require,module,exports){
module.exports=require(15)
},{}],4622:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4619,"./lib/window.console.js":4621}],4623:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4624,"./lib/timers.js":4625}],4624:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4613,"polyfill/polyfill-base.js":4628}],4625:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4628}],4626:[function(require,module,exports){
module.exports=require(14)
},{}],4627:[function(require,module,exports){
module.exports=require(15)
},{}],4628:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4626,"./lib/window.console.js":4627}],4629:[function(require,module,exports){
module.exports=require(66)
},{}],4630:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":4629}],4631:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":4629}],4632:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":4629}],4633:[function(require,module,exports){
module.exports=require(14)
},{}],4634:[function(require,module,exports){
module.exports=require(15)
},{}],4635:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4633,"./lib/window.console.js":4634}],4636:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4637,"./lib/timers.js":4638}],4637:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4613,"polyfill/polyfill-base.js":4641}],4638:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4641}],4639:[function(require,module,exports){
module.exports=require(14)
},{}],4640:[function(require,module,exports){
module.exports=require(15)
},{}],4641:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4639,"./lib/window.console.js":4640}],4642:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":4643}],4643:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":4644,"js-ext/lib/object.js":4645}],4644:[function(require,module,exports){
module.exports=require(4)
},{}],4645:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4644,"polyfill/polyfill-base.js":4648,"utils":4649}],4646:[function(require,module,exports){
module.exports=require(14)
},{}],4647:[function(require,module,exports){
module.exports=require(15)
},{}],4648:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4646,"./lib/window.console.js":4647}],4649:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4650,"./lib/timers.js":4651}],4650:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4644,"polyfill/polyfill-base.js":4654}],4651:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4654}],4652:[function(require,module,exports){
module.exports=require(14)
},{}],4653:[function(require,module,exports){
module.exports=require(15)
},{}],4654:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4652,"./lib/window.console.js":4653}],4655:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":4613,"js-ext/lib/object.js":4616,"js-ext/lib/string.js":4618,"polyfill":4635,"polyfill/extra/transition.js":4630,"polyfill/extra/vendorCSS.js":4632}],4656:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":4613,"js-ext/lib/object.js":4616,"polyfill":4635}],4657:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":4613,"js-ext/lib/object.js":4616,"js-ext/lib/string.js":4618,"polyfill":4635}],4658:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":4611,"./attribute-extractor.js":4655,"./element-array.js":4656,"./html-parser.js":4659,"./node-parser.js":4660,"./vdom-ns.js":4661,"./vnode.js":4662,"js-ext/extra/hashmap.js":4613,"js-ext/lib/object.js":4616,"js-ext/lib/promise.js":4617,"js-ext/lib/string.js":4618,"polyfill":4635,"polyfill/extra/transition.js":4630,"polyfill/extra/transitionend.js":4631,"polyfill/extra/vendorCSS.js":4632,"utils":4636,"window-ext":4642}],4659:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":4655,"./vdom-ns.js":4661,"js-ext/extra/hashmap.js":4613,"js-ext/lib/object.js":4616,"polyfill":4635}],4660:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":4655,"./vdom-ns.js":4661,"./vnode.js":4662,"js-ext/extra/hashmap.js":4613,"js-ext/lib/object.js":4616,"polyfill":4635}],4661:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":4613,"js-ext/lib/object.js":4616,"polyfill":4635}],4662:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":4655,"./html-parser.js":4659,"./vdom-ns.js":4661,"js-ext/extra/hashmap.js":4613,"js-ext/extra/lightmap.js":4614,"js-ext/lib/array.js":4615,"js-ext/lib/object.js":4616,"js-ext/lib/string.js":4618,"polyfill":4635,"utils/lib/timers.js":4638}],4663:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":4657,"./partials/extend-element.js":4658,"./partials/node-parser.js":4660,"js-ext/extra/hashmap.js":4613,"js-ext/lib/object.js":4616,"utils/lib/timers.js":4638}],4664:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":4666,"js-ext/extra/hashmap.js":4665,"polyfill/polyfill-base.js":4671}],4665:[function(require,module,exports){
module.exports=require(4)
},{}],4666:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4665,"polyfill/polyfill-base.js":4671,"utils":4672}],4667:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":4671}],4668:[function(require,module,exports){
module.exports=require(29)
},{}],4669:[function(require,module,exports){
module.exports=require(14)
},{}],4670:[function(require,module,exports){
module.exports=require(15)
},{}],4671:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4669,"./lib/window.console.js":4670}],4672:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4673,"./lib/timers.js":4674}],4673:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4665,"polyfill/polyfill-base.js":4677}],4674:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4677}],4675:[function(require,module,exports){
module.exports=require(14)
},{}],4676:[function(require,module,exports){
module.exports=require(15)
},{}],4677:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4675,"./lib/window.console.js":4676}],4678:[function(require,module,exports){
module.exports=require(14)
},{}],4679:[function(require,module,exports){
module.exports=require(15)
},{}],4680:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4678,"./lib/window.console.js":4679}],4681:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4684}],4682:[function(require,module,exports){
module.exports=require(14)
},{}],4683:[function(require,module,exports){
module.exports=require(15)
},{}],4684:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4682,"./lib/window.console.js":4683}],4685:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],4686:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":4690,"js-ext/extra/hashmap.js":4687,"polyfill/polyfill-base.js":4696}],4687:[function(require,module,exports){
module.exports=require(4)
},{}],4688:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":4689,"../lib/object.js":4690,"./classes.js":4686,"js-ext/extra/hashmap.js":4687,"polyfill/lib/weakmap.js":4694}],4689:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":4696}],4690:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4687,"polyfill/polyfill-base.js":4696,"utils":4697}],4691:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":4696}],4692:[function(require,module,exports){
module.exports=require(29)
},{}],4693:[function(require,module,exports){
module.exports=require(14)
},{}],4694:[function(require,module,exports){
module.exports=require(57)
},{}],4695:[function(require,module,exports){
module.exports=require(15)
},{}],4696:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4693,"./lib/window.console.js":4695}],4697:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4698,"./lib/timers.js":4699}],4698:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4687,"polyfill/polyfill-base.js":4702}],4699:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4702}],4700:[function(require,module,exports){
module.exports=require(14)
},{}],4701:[function(require,module,exports){
module.exports=require(15)
},{}],4702:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4700,"./lib/window.console.js":4701}],4703:[function(require,module,exports){
module.exports=require(66)
},{}],4704:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":4703}],4705:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":4703}],4706:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":4703}],4707:[function(require,module,exports){
module.exports=require(14)
},{}],4708:[function(require,module,exports){
module.exports=require(15)
},{}],4709:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4707,"./lib/window.console.js":4708}],4710:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4711,"./lib/timers.js":4712}],4711:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4687,"polyfill/polyfill-base.js":4715}],4712:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4715}],4713:[function(require,module,exports){
module.exports=require(14)
},{}],4714:[function(require,module,exports){
module.exports=require(15)
},{}],4715:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4713,"./lib/window.console.js":4714}],4716:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":4717}],4717:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":4718,"js-ext/lib/object.js":4719}],4718:[function(require,module,exports){
module.exports=require(4)
},{}],4719:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4718,"polyfill/polyfill-base.js":4722,"utils":4723}],4720:[function(require,module,exports){
module.exports=require(14)
},{}],4721:[function(require,module,exports){
module.exports=require(15)
},{}],4722:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4720,"./lib/window.console.js":4721}],4723:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4724,"./lib/timers.js":4725}],4724:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4718,"polyfill/polyfill-base.js":4728}],4725:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4728}],4726:[function(require,module,exports){
module.exports=require(14)
},{}],4727:[function(require,module,exports){
module.exports=require(15)
},{}],4728:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4726,"./lib/window.console.js":4727}],4729:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":4687,"js-ext/lib/object.js":4690,"js-ext/lib/string.js":4692,"polyfill":4709,"polyfill/extra/transition.js":4704,"polyfill/extra/vendorCSS.js":4706}],4730:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":4687,"js-ext/lib/object.js":4690,"polyfill":4709}],4731:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":4687,"js-ext/lib/object.js":4690,"js-ext/lib/string.js":4692,"polyfill":4709}],4732:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":4685,"./attribute-extractor.js":4729,"./element-array.js":4730,"./html-parser.js":4733,"./node-parser.js":4734,"./vdom-ns.js":4735,"./vnode.js":4736,"js-ext/extra/hashmap.js":4687,"js-ext/lib/object.js":4690,"js-ext/lib/promise.js":4691,"js-ext/lib/string.js":4692,"polyfill":4709,"polyfill/extra/transition.js":4704,"polyfill/extra/transitionend.js":4705,"polyfill/extra/vendorCSS.js":4706,"utils":4710,"window-ext":4716}],4733:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":4729,"./vdom-ns.js":4735,"js-ext/extra/hashmap.js":4687,"js-ext/lib/object.js":4690,"polyfill":4709}],4734:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":4729,"./vdom-ns.js":4735,"./vnode.js":4736,"js-ext/extra/hashmap.js":4687,"js-ext/lib/object.js":4690,"polyfill":4709}],4735:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":4687,"js-ext/lib/object.js":4690,"polyfill":4709}],4736:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":4729,"./html-parser.js":4733,"./vdom-ns.js":4735,"js-ext/extra/hashmap.js":4687,"js-ext/extra/lightmap.js":4688,"js-ext/lib/array.js":4689,"js-ext/lib/object.js":4690,"js-ext/lib/string.js":4692,"polyfill":4709,"utils/lib/timers.js":4712}],4737:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":4731,"./partials/extend-element.js":4732,"./partials/node-parser.js":4734,"js-ext/extra/hashmap.js":4687,"js-ext/lib/object.js":4690,"utils/lib/timers.js":4712}],4738:[function(require,module,exports){
module.exports=require(14)
},{}],4739:[function(require,module,exports){
module.exports=require(15)
},{}],4740:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4738,"./lib/window.console.js":4739}],4741:[function(require,module,exports){
module.exports=require(4)
},{}],4742:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4741,"polyfill/polyfill-base.js":4746,"utils":4747}],4743:[function(require,module,exports){
module.exports=require(29)
},{}],4744:[function(require,module,exports){
module.exports=require(14)
},{}],4745:[function(require,module,exports){
module.exports=require(15)
},{}],4746:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4744,"./lib/window.console.js":4745}],4747:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4748,"./lib/timers.js":4749}],4748:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4741,"polyfill/polyfill-base.js":4752}],4749:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4752}],4750:[function(require,module,exports){
module.exports=require(14)
},{}],4751:[function(require,module,exports){
module.exports=require(15)
},{}],4752:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4750,"./lib/window.console.js":4751}],4753:[function(require,module,exports){
module.exports=require(14)
},{}],4754:[function(require,module,exports){
module.exports=require(15)
},{}],4755:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4753,"./lib/window.console.js":4754}],4756:[function(require,module,exports){
module.exports=require(567)
},{"js-ext/extra/hashmap.js":4741,"js-ext/lib/object.js":4742,"js-ext/lib/string.js":4743,"polyfill":4755}],4757:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],4758:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":4762,"js-ext/extra/hashmap.js":4759,"polyfill/polyfill-base.js":4768}],4759:[function(require,module,exports){
module.exports=require(4)
},{}],4760:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":4761,"../lib/object.js":4762,"./classes.js":4758,"js-ext/extra/hashmap.js":4759,"polyfill/lib/weakmap.js":4766}],4761:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":4768}],4762:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4759,"polyfill/polyfill-base.js":4768,"utils":4769}],4763:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":4768}],4764:[function(require,module,exports){
module.exports=require(29)
},{}],4765:[function(require,module,exports){
module.exports=require(14)
},{}],4766:[function(require,module,exports){
module.exports=require(57)
},{}],4767:[function(require,module,exports){
module.exports=require(15)
},{}],4768:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4765,"./lib/window.console.js":4767}],4769:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4770,"./lib/timers.js":4771}],4770:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4759,"polyfill/polyfill-base.js":4774}],4771:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4774}],4772:[function(require,module,exports){
module.exports=require(14)
},{}],4773:[function(require,module,exports){
module.exports=require(15)
},{}],4774:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4772,"./lib/window.console.js":4773}],4775:[function(require,module,exports){
module.exports=require(66)
},{}],4776:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":4775}],4777:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":4775}],4778:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":4775}],4779:[function(require,module,exports){
module.exports=require(14)
},{}],4780:[function(require,module,exports){
module.exports=require(15)
},{}],4781:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4779,"./lib/window.console.js":4780}],4782:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4783,"./lib/timers.js":4784}],4783:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4759,"polyfill/polyfill-base.js":4787}],4784:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4787}],4785:[function(require,module,exports){
module.exports=require(14)
},{}],4786:[function(require,module,exports){
module.exports=require(15)
},{}],4787:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4785,"./lib/window.console.js":4786}],4788:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":4789}],4789:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":4790,"js-ext/lib/object.js":4791}],4790:[function(require,module,exports){
module.exports=require(4)
},{}],4791:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4790,"polyfill/polyfill-base.js":4794,"utils":4795}],4792:[function(require,module,exports){
module.exports=require(14)
},{}],4793:[function(require,module,exports){
module.exports=require(15)
},{}],4794:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4792,"./lib/window.console.js":4793}],4795:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4796,"./lib/timers.js":4797}],4796:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4790,"polyfill/polyfill-base.js":4800}],4797:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4800}],4798:[function(require,module,exports){
module.exports=require(14)
},{}],4799:[function(require,module,exports){
module.exports=require(15)
},{}],4800:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4798,"./lib/window.console.js":4799}],4801:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":4759,"js-ext/lib/object.js":4762,"js-ext/lib/string.js":4764,"polyfill":4781,"polyfill/extra/transition.js":4776,"polyfill/extra/vendorCSS.js":4778}],4802:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":4759,"js-ext/lib/object.js":4762,"polyfill":4781}],4803:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":4759,"js-ext/lib/object.js":4762,"js-ext/lib/string.js":4764,"polyfill":4781}],4804:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":4757,"./attribute-extractor.js":4801,"./element-array.js":4802,"./html-parser.js":4805,"./node-parser.js":4806,"./vdom-ns.js":4807,"./vnode.js":4808,"js-ext/extra/hashmap.js":4759,"js-ext/lib/object.js":4762,"js-ext/lib/promise.js":4763,"js-ext/lib/string.js":4764,"polyfill":4781,"polyfill/extra/transition.js":4776,"polyfill/extra/transitionend.js":4777,"polyfill/extra/vendorCSS.js":4778,"utils":4782,"window-ext":4788}],4805:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":4801,"./vdom-ns.js":4807,"js-ext/extra/hashmap.js":4759,"js-ext/lib/object.js":4762,"polyfill":4781}],4806:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":4801,"./vdom-ns.js":4807,"./vnode.js":4808,"js-ext/extra/hashmap.js":4759,"js-ext/lib/object.js":4762,"polyfill":4781}],4807:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":4759,"js-ext/lib/object.js":4762,"polyfill":4781}],4808:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":4801,"./html-parser.js":4805,"./vdom-ns.js":4807,"js-ext/extra/hashmap.js":4759,"js-ext/extra/lightmap.js":4760,"js-ext/lib/array.js":4761,"js-ext/lib/object.js":4762,"js-ext/lib/string.js":4764,"polyfill":4781,"utils/lib/timers.js":4784}],4809:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":4803,"./partials/extend-element.js":4804,"./partials/node-parser.js":4806,"js-ext/extra/hashmap.js":4759,"js-ext/lib/object.js":4762,"utils/lib/timers.js":4784}],4810:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":4811}],4811:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":4812,"js-ext/lib/object.js":4813}],4812:[function(require,module,exports){
module.exports=require(4)
},{}],4813:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4812,"polyfill/polyfill-base.js":4816,"utils":4817}],4814:[function(require,module,exports){
module.exports=require(14)
},{}],4815:[function(require,module,exports){
module.exports=require(15)
},{}],4816:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4814,"./lib/window.console.js":4815}],4817:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4818,"./lib/timers.js":4819}],4818:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4812,"polyfill/polyfill-base.js":4822}],4819:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4822}],4820:[function(require,module,exports){
module.exports=require(14)
},{}],4821:[function(require,module,exports){
module.exports=require(15)
},{}],4822:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4820,"./lib/window.console.js":4821}],4823:[function(require,module,exports){
module.exports=require(634)
},{"./lib/hammer-2.0.4.js":4824,"event-dom":4825}],4824:[function(require,module,exports){
module.exports=require(635)
},{"utils":4920}],4825:[function(require,module,exports){
module.exports=require(267)
},{"event":4829,"js-ext/extra/hashmap.js":4845,"js-ext/lib/array.js":4846,"js-ext/lib/object.js":4847,"js-ext/lib/string.js":4848,"polyfill/polyfill-base.js":4860,"utils":4861,"vdom":4919}],4826:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":4831,"js-ext/lib/object.js":4832,"polyfill/polyfill-base.js":4844}],4827:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":4826}],4828:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":4826,"js-ext/extra/classes.js":4830,"js-ext/lib/object.js":4832}],4829:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":4826,"./event-emitter.js":4827,"./event-listener.js":4828}],4830:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":4832,"js-ext/extra/hashmap.js":4831,"polyfill/polyfill-base.js":4835}],4831:[function(require,module,exports){
module.exports=require(4)
},{}],4832:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4831,"polyfill/polyfill-base.js":4835,"utils":4836}],4833:[function(require,module,exports){
module.exports=require(14)
},{}],4834:[function(require,module,exports){
module.exports=require(15)
},{}],4835:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4833,"./lib/window.console.js":4834}],4836:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4837,"./lib/timers.js":4838}],4837:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4831,"polyfill/polyfill-base.js":4841}],4838:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4841}],4839:[function(require,module,exports){
module.exports=require(14)
},{}],4840:[function(require,module,exports){
module.exports=require(15)
},{}],4841:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4839,"./lib/window.console.js":4840}],4842:[function(require,module,exports){
module.exports=require(14)
},{}],4843:[function(require,module,exports){
module.exports=require(15)
},{}],4844:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4842,"./lib/window.console.js":4843}],4845:[function(require,module,exports){
module.exports=require(4)
},{}],4846:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":4851}],4847:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4845,"polyfill/polyfill-base.js":4851,"utils":4852}],4848:[function(require,module,exports){
module.exports=require(29)
},{}],4849:[function(require,module,exports){
module.exports=require(14)
},{}],4850:[function(require,module,exports){
module.exports=require(15)
},{}],4851:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4849,"./lib/window.console.js":4850}],4852:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4853,"./lib/timers.js":4854}],4853:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4845,"polyfill/polyfill-base.js":4857}],4854:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4857}],4855:[function(require,module,exports){
module.exports=require(14)
},{}],4856:[function(require,module,exports){
module.exports=require(15)
},{}],4857:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4855,"./lib/window.console.js":4856}],4858:[function(require,module,exports){
module.exports=require(14)
},{}],4859:[function(require,module,exports){
module.exports=require(15)
},{}],4860:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4858,"./lib/window.console.js":4859}],4861:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4862,"./lib/timers.js":4863}],4862:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4845,"polyfill/polyfill-base.js":4866}],4863:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4866}],4864:[function(require,module,exports){
module.exports=require(14)
},{}],4865:[function(require,module,exports){
module.exports=require(15)
},{}],4866:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4864,"./lib/window.console.js":4865}],4867:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],4868:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":4872,"js-ext/extra/hashmap.js":4869,"polyfill/polyfill-base.js":4878}],4869:[function(require,module,exports){
module.exports=require(4)
},{}],4870:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":4871,"../lib/object.js":4872,"./classes.js":4868,"js-ext/extra/hashmap.js":4869,"polyfill/lib/weakmap.js":4876}],4871:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":4878}],4872:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4869,"polyfill/polyfill-base.js":4878,"utils":4879}],4873:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":4878}],4874:[function(require,module,exports){
module.exports=require(29)
},{}],4875:[function(require,module,exports){
module.exports=require(14)
},{}],4876:[function(require,module,exports){
module.exports=require(57)
},{}],4877:[function(require,module,exports){
module.exports=require(15)
},{}],4878:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4875,"./lib/window.console.js":4877}],4879:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4880,"./lib/timers.js":4881}],4880:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4869,"polyfill/polyfill-base.js":4884}],4881:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4884}],4882:[function(require,module,exports){
module.exports=require(14)
},{}],4883:[function(require,module,exports){
module.exports=require(15)
},{}],4884:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4882,"./lib/window.console.js":4883}],4885:[function(require,module,exports){
module.exports=require(66)
},{}],4886:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":4885}],4887:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":4885}],4888:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":4885}],4889:[function(require,module,exports){
module.exports=require(14)
},{}],4890:[function(require,module,exports){
module.exports=require(15)
},{}],4891:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4889,"./lib/window.console.js":4890}],4892:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4893,"./lib/timers.js":4894}],4893:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4869,"polyfill/polyfill-base.js":4897}],4894:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4897}],4895:[function(require,module,exports){
module.exports=require(14)
},{}],4896:[function(require,module,exports){
module.exports=require(15)
},{}],4897:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4895,"./lib/window.console.js":4896}],4898:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":4899}],4899:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":4900,"js-ext/lib/object.js":4901}],4900:[function(require,module,exports){
module.exports=require(4)
},{}],4901:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4900,"polyfill/polyfill-base.js":4904,"utils":4905}],4902:[function(require,module,exports){
module.exports=require(14)
},{}],4903:[function(require,module,exports){
module.exports=require(15)
},{}],4904:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4902,"./lib/window.console.js":4903}],4905:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4906,"./lib/timers.js":4907}],4906:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4900,"polyfill/polyfill-base.js":4910}],4907:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4910}],4908:[function(require,module,exports){
module.exports=require(14)
},{}],4909:[function(require,module,exports){
module.exports=require(15)
},{}],4910:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4908,"./lib/window.console.js":4909}],4911:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":4869,"js-ext/lib/object.js":4872,"js-ext/lib/string.js":4874,"polyfill":4891,"polyfill/extra/transition.js":4886,"polyfill/extra/vendorCSS.js":4888}],4912:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":4869,"js-ext/lib/object.js":4872,"polyfill":4891}],4913:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":4869,"js-ext/lib/object.js":4872,"js-ext/lib/string.js":4874,"polyfill":4891}],4914:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":4867,"./attribute-extractor.js":4911,"./element-array.js":4912,"./html-parser.js":4915,"./node-parser.js":4916,"./vdom-ns.js":4917,"./vnode.js":4918,"js-ext/extra/hashmap.js":4869,"js-ext/lib/object.js":4872,"js-ext/lib/promise.js":4873,"js-ext/lib/string.js":4874,"polyfill":4891,"polyfill/extra/transition.js":4886,"polyfill/extra/transitionend.js":4887,"polyfill/extra/vendorCSS.js":4888,"utils":4892,"window-ext":4898}],4915:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":4911,"./vdom-ns.js":4917,"js-ext/extra/hashmap.js":4869,"js-ext/lib/object.js":4872,"polyfill":4891}],4916:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":4911,"./vdom-ns.js":4917,"./vnode.js":4918,"js-ext/extra/hashmap.js":4869,"js-ext/lib/object.js":4872,"polyfill":4891}],4917:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":4869,"js-ext/lib/object.js":4872,"polyfill":4891}],4918:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":4911,"./html-parser.js":4915,"./vdom-ns.js":4917,"js-ext/extra/hashmap.js":4869,"js-ext/extra/lightmap.js":4870,"js-ext/lib/array.js":4871,"js-ext/lib/object.js":4872,"js-ext/lib/string.js":4874,"polyfill":4891,"utils/lib/timers.js":4894}],4919:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":4913,"./partials/extend-element.js":4914,"./partials/node-parser.js":4916,"js-ext/extra/hashmap.js":4869,"js-ext/lib/object.js":4872,"utils/lib/timers.js":4894}],4920:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4921,"./lib/timers.js":4922}],4921:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4926,"polyfill/polyfill-base.js":4925}],4922:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4925}],4923:[function(require,module,exports){
module.exports=require(14)
},{}],4924:[function(require,module,exports){
module.exports=require(15)
},{}],4925:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4923,"./lib/window.console.js":4924}],4926:[function(require,module,exports){
module.exports=require(4)
},{}],4927:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4926,"polyfill/polyfill-base.js":4930,"utils":4931}],4928:[function(require,module,exports){
module.exports=require(14)
},{}],4929:[function(require,module,exports){
module.exports=require(15)
},{}],4930:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4928,"./lib/window.console.js":4929}],4931:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4932,"./lib/timers.js":4933}],4932:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4926,"polyfill/polyfill-base.js":4936}],4933:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4936}],4934:[function(require,module,exports){
module.exports=require(14)
},{}],4935:[function(require,module,exports){
module.exports=require(15)
},{}],4936:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4934,"./lib/window.console.js":4935}],4937:[function(require,module,exports){
arguments[4][5][0].apply(exports,arguments)
},{"event-dom":4938,"js-ext/extra/classes.js":5033,"js-ext/extra/hashmap.js":5034,"js-ext/lib/object.js":5035,"js-ext/lib/promise.js":5036,"js-ext/lib/string.js":5037,"polyfill":5049,"utils/lib/timers.js":5050,"vdom":5106}],4938:[function(require,module,exports){
module.exports=require(267)
},{"event":4942,"js-ext/extra/hashmap.js":4958,"js-ext/lib/array.js":4959,"js-ext/lib/object.js":4960,"js-ext/lib/string.js":4961,"polyfill/polyfill-base.js":4973,"utils":4974,"vdom":5032}],4939:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":4944,"js-ext/lib/object.js":4945,"polyfill/polyfill-base.js":4957}],4940:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":4939}],4941:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":4939,"js-ext/extra/classes.js":4943,"js-ext/lib/object.js":4945}],4942:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":4939,"./event-emitter.js":4940,"./event-listener.js":4941}],4943:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":4945,"js-ext/extra/hashmap.js":4944,"polyfill/polyfill-base.js":4948}],4944:[function(require,module,exports){
module.exports=require(4)
},{}],4945:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4944,"polyfill/polyfill-base.js":4948,"utils":4949}],4946:[function(require,module,exports){
module.exports=require(14)
},{}],4947:[function(require,module,exports){
module.exports=require(15)
},{}],4948:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4946,"./lib/window.console.js":4947}],4949:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4950,"./lib/timers.js":4951}],4950:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4944,"polyfill/polyfill-base.js":4954}],4951:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4954}],4952:[function(require,module,exports){
module.exports=require(14)
},{}],4953:[function(require,module,exports){
module.exports=require(15)
},{}],4954:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4952,"./lib/window.console.js":4953}],4955:[function(require,module,exports){
module.exports=require(14)
},{}],4956:[function(require,module,exports){
module.exports=require(15)
},{}],4957:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4955,"./lib/window.console.js":4956}],4958:[function(require,module,exports){
module.exports=require(4)
},{}],4959:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":4964}],4960:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4958,"polyfill/polyfill-base.js":4964,"utils":4965}],4961:[function(require,module,exports){
module.exports=require(29)
},{}],4962:[function(require,module,exports){
module.exports=require(14)
},{}],4963:[function(require,module,exports){
module.exports=require(15)
},{}],4964:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4962,"./lib/window.console.js":4963}],4965:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4966,"./lib/timers.js":4967}],4966:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4958,"polyfill/polyfill-base.js":4970}],4967:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4970}],4968:[function(require,module,exports){
module.exports=require(14)
},{}],4969:[function(require,module,exports){
module.exports=require(15)
},{}],4970:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4968,"./lib/window.console.js":4969}],4971:[function(require,module,exports){
module.exports=require(14)
},{}],4972:[function(require,module,exports){
module.exports=require(15)
},{}],4973:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4971,"./lib/window.console.js":4972}],4974:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4975,"./lib/timers.js":4976}],4975:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4958,"polyfill/polyfill-base.js":4979}],4976:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4979}],4977:[function(require,module,exports){
module.exports=require(14)
},{}],4978:[function(require,module,exports){
module.exports=require(15)
},{}],4979:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4977,"./lib/window.console.js":4978}],4980:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],4981:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":4985,"js-ext/extra/hashmap.js":4982,"polyfill/polyfill-base.js":4991}],4982:[function(require,module,exports){
module.exports=require(4)
},{}],4983:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":4984,"../lib/object.js":4985,"./classes.js":4981,"js-ext/extra/hashmap.js":4982,"polyfill/lib/weakmap.js":4989}],4984:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":4991}],4985:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":4982,"polyfill/polyfill-base.js":4991,"utils":4992}],4986:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":4991}],4987:[function(require,module,exports){
module.exports=require(29)
},{}],4988:[function(require,module,exports){
module.exports=require(14)
},{}],4989:[function(require,module,exports){
module.exports=require(57)
},{}],4990:[function(require,module,exports){
module.exports=require(15)
},{}],4991:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4988,"./lib/window.console.js":4990}],4992:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":4993,"./lib/timers.js":4994}],4993:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4982,"polyfill/polyfill-base.js":4997}],4994:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":4997}],4995:[function(require,module,exports){
module.exports=require(14)
},{}],4996:[function(require,module,exports){
module.exports=require(15)
},{}],4997:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":4995,"./lib/window.console.js":4996}],4998:[function(require,module,exports){
module.exports=require(66)
},{}],4999:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":4998}],5000:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":4998}],5001:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":4998}],5002:[function(require,module,exports){
module.exports=require(14)
},{}],5003:[function(require,module,exports){
module.exports=require(15)
},{}],5004:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5002,"./lib/window.console.js":5003}],5005:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5006,"./lib/timers.js":5007}],5006:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4982,"polyfill/polyfill-base.js":5010}],5007:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5010}],5008:[function(require,module,exports){
module.exports=require(14)
},{}],5009:[function(require,module,exports){
module.exports=require(15)
},{}],5010:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5008,"./lib/window.console.js":5009}],5011:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":5012}],5012:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":5013,"js-ext/lib/object.js":5014}],5013:[function(require,module,exports){
module.exports=require(4)
},{}],5014:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5013,"polyfill/polyfill-base.js":5017,"utils":5018}],5015:[function(require,module,exports){
module.exports=require(14)
},{}],5016:[function(require,module,exports){
module.exports=require(15)
},{}],5017:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5015,"./lib/window.console.js":5016}],5018:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5019,"./lib/timers.js":5020}],5019:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5013,"polyfill/polyfill-base.js":5023}],5020:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5023}],5021:[function(require,module,exports){
module.exports=require(14)
},{}],5022:[function(require,module,exports){
module.exports=require(15)
},{}],5023:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5021,"./lib/window.console.js":5022}],5024:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":4982,"js-ext/lib/object.js":4985,"js-ext/lib/string.js":4987,"polyfill":5004,"polyfill/extra/transition.js":4999,"polyfill/extra/vendorCSS.js":5001}],5025:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":4982,"js-ext/lib/object.js":4985,"polyfill":5004}],5026:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":4982,"js-ext/lib/object.js":4985,"js-ext/lib/string.js":4987,"polyfill":5004}],5027:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":4980,"./attribute-extractor.js":5024,"./element-array.js":5025,"./html-parser.js":5028,"./node-parser.js":5029,"./vdom-ns.js":5030,"./vnode.js":5031,"js-ext/extra/hashmap.js":4982,"js-ext/lib/object.js":4985,"js-ext/lib/promise.js":4986,"js-ext/lib/string.js":4987,"polyfill":5004,"polyfill/extra/transition.js":4999,"polyfill/extra/transitionend.js":5000,"polyfill/extra/vendorCSS.js":5001,"utils":5005,"window-ext":5011}],5028:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":5024,"./vdom-ns.js":5030,"js-ext/extra/hashmap.js":4982,"js-ext/lib/object.js":4985,"polyfill":5004}],5029:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":5024,"./vdom-ns.js":5030,"./vnode.js":5031,"js-ext/extra/hashmap.js":4982,"js-ext/lib/object.js":4985,"polyfill":5004}],5030:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":4982,"js-ext/lib/object.js":4985,"polyfill":5004}],5031:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":5024,"./html-parser.js":5028,"./vdom-ns.js":5030,"js-ext/extra/hashmap.js":4982,"js-ext/extra/lightmap.js":4983,"js-ext/lib/array.js":4984,"js-ext/lib/object.js":4985,"js-ext/lib/string.js":4987,"polyfill":5004,"utils/lib/timers.js":5007}],5032:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":5026,"./partials/extend-element.js":5027,"./partials/node-parser.js":5029,"js-ext/extra/hashmap.js":4982,"js-ext/lib/object.js":4985,"utils/lib/timers.js":5007}],5033:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":5035,"js-ext/extra/hashmap.js":5034,"polyfill/polyfill-base.js":5040}],5034:[function(require,module,exports){
module.exports=require(4)
},{}],5035:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5034,"polyfill/polyfill-base.js":5040,"utils":5041}],5036:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":5040}],5037:[function(require,module,exports){
module.exports=require(29)
},{}],5038:[function(require,module,exports){
module.exports=require(14)
},{}],5039:[function(require,module,exports){
module.exports=require(15)
},{}],5040:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5038,"./lib/window.console.js":5039}],5041:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5042,"./lib/timers.js":5043}],5042:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5034,"polyfill/polyfill-base.js":5046}],5043:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5046}],5044:[function(require,module,exports){
module.exports=require(14)
},{}],5045:[function(require,module,exports){
module.exports=require(15)
},{}],5046:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5044,"./lib/window.console.js":5045}],5047:[function(require,module,exports){
module.exports=require(14)
},{}],5048:[function(require,module,exports){
module.exports=require(15)
},{}],5049:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5047,"./lib/window.console.js":5048}],5050:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5053}],5051:[function(require,module,exports){
module.exports=require(14)
},{}],5052:[function(require,module,exports){
module.exports=require(15)
},{}],5053:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5051,"./lib/window.console.js":5052}],5054:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],5055:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":5059,"js-ext/extra/hashmap.js":5056,"polyfill/polyfill-base.js":5065}],5056:[function(require,module,exports){
module.exports=require(4)
},{}],5057:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":5058,"../lib/object.js":5059,"./classes.js":5055,"js-ext/extra/hashmap.js":5056,"polyfill/lib/weakmap.js":5063}],5058:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":5065}],5059:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5056,"polyfill/polyfill-base.js":5065,"utils":5066}],5060:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":5065}],5061:[function(require,module,exports){
module.exports=require(29)
},{}],5062:[function(require,module,exports){
module.exports=require(14)
},{}],5063:[function(require,module,exports){
module.exports=require(57)
},{}],5064:[function(require,module,exports){
module.exports=require(15)
},{}],5065:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5062,"./lib/window.console.js":5064}],5066:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5067,"./lib/timers.js":5068}],5067:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5056,"polyfill/polyfill-base.js":5071}],5068:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5071}],5069:[function(require,module,exports){
module.exports=require(14)
},{}],5070:[function(require,module,exports){
module.exports=require(15)
},{}],5071:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5069,"./lib/window.console.js":5070}],5072:[function(require,module,exports){
module.exports=require(66)
},{}],5073:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":5072}],5074:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":5072}],5075:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":5072}],5076:[function(require,module,exports){
module.exports=require(14)
},{}],5077:[function(require,module,exports){
module.exports=require(15)
},{}],5078:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5076,"./lib/window.console.js":5077}],5079:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5080,"./lib/timers.js":5081}],5080:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5056,"polyfill/polyfill-base.js":5084}],5081:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5084}],5082:[function(require,module,exports){
module.exports=require(14)
},{}],5083:[function(require,module,exports){
module.exports=require(15)
},{}],5084:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5082,"./lib/window.console.js":5083}],5085:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":5086}],5086:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":5087,"js-ext/lib/object.js":5088}],5087:[function(require,module,exports){
module.exports=require(4)
},{}],5088:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5087,"polyfill/polyfill-base.js":5091,"utils":5092}],5089:[function(require,module,exports){
module.exports=require(14)
},{}],5090:[function(require,module,exports){
module.exports=require(15)
},{}],5091:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5089,"./lib/window.console.js":5090}],5092:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5093,"./lib/timers.js":5094}],5093:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5087,"polyfill/polyfill-base.js":5097}],5094:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5097}],5095:[function(require,module,exports){
module.exports=require(14)
},{}],5096:[function(require,module,exports){
module.exports=require(15)
},{}],5097:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5095,"./lib/window.console.js":5096}],5098:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":5056,"js-ext/lib/object.js":5059,"js-ext/lib/string.js":5061,"polyfill":5078,"polyfill/extra/transition.js":5073,"polyfill/extra/vendorCSS.js":5075}],5099:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":5056,"js-ext/lib/object.js":5059,"polyfill":5078}],5100:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":5056,"js-ext/lib/object.js":5059,"js-ext/lib/string.js":5061,"polyfill":5078}],5101:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":5054,"./attribute-extractor.js":5098,"./element-array.js":5099,"./html-parser.js":5102,"./node-parser.js":5103,"./vdom-ns.js":5104,"./vnode.js":5105,"js-ext/extra/hashmap.js":5056,"js-ext/lib/object.js":5059,"js-ext/lib/promise.js":5060,"js-ext/lib/string.js":5061,"polyfill":5078,"polyfill/extra/transition.js":5073,"polyfill/extra/transitionend.js":5074,"polyfill/extra/vendorCSS.js":5075,"utils":5079,"window-ext":5085}],5102:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":5098,"./vdom-ns.js":5104,"js-ext/extra/hashmap.js":5056,"js-ext/lib/object.js":5059,"polyfill":5078}],5103:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":5098,"./vdom-ns.js":5104,"./vnode.js":5105,"js-ext/extra/hashmap.js":5056,"js-ext/lib/object.js":5059,"polyfill":5078}],5104:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":5056,"js-ext/lib/object.js":5059,"polyfill":5078}],5105:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":5098,"./html-parser.js":5102,"./vdom-ns.js":5104,"js-ext/extra/hashmap.js":5056,"js-ext/extra/lightmap.js":5057,"js-ext/lib/array.js":5058,"js-ext/lib/object.js":5059,"js-ext/lib/string.js":5061,"polyfill":5078,"utils/lib/timers.js":5081}],5106:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":5100,"./partials/extend-element.js":5101,"./partials/node-parser.js":5103,"js-ext/extra/hashmap.js":5056,"js-ext/lib/object.js":5059,"utils/lib/timers.js":5081}],5107:[function(require,module,exports){
module.exports=require(14)
},{}],5108:[function(require,module,exports){
module.exports=require(15)
},{}],5109:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5107,"./lib/window.console.js":5108}],5110:[function(require,module,exports){
module.exports=require(4)
},{}],5111:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5110,"polyfill/polyfill-base.js":5115,"utils":5116}],5112:[function(require,module,exports){
module.exports=require(29)
},{}],5113:[function(require,module,exports){
module.exports=require(14)
},{}],5114:[function(require,module,exports){
module.exports=require(15)
},{}],5115:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5113,"./lib/window.console.js":5114}],5116:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5117,"./lib/timers.js":5118}],5117:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5110,"polyfill/polyfill-base.js":5121}],5118:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5121}],5119:[function(require,module,exports){
module.exports=require(14)
},{}],5120:[function(require,module,exports){
module.exports=require(15)
},{}],5121:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5119,"./lib/window.console.js":5120}],5122:[function(require,module,exports){
module.exports=require(14)
},{}],5123:[function(require,module,exports){
module.exports=require(15)
},{}],5124:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5122,"./lib/window.console.js":5123}],5125:[function(require,module,exports){
module.exports=require(567)
},{"js-ext/extra/hashmap.js":5110,"js-ext/lib/object.js":5111,"js-ext/lib/string.js":5112,"polyfill":5124}],5126:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5127,"./lib/timers.js":5128}],5127:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":4926,"polyfill/polyfill-base.js":5131}],5128:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5131}],5129:[function(require,module,exports){
module.exports=require(14)
},{}],5130:[function(require,module,exports){
module.exports=require(15)
},{}],5131:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5129,"./lib/window.console.js":5130}],5132:[function(require,module,exports){
module.exports=require(1913)
},{"./css/scrollable.css":4453,"drag":4455,"event-mobile":4823,"js-ext/extra/hashmap.js":4926,"js-ext/lib/object.js":4927,"node-plugin":4937,"polyfill":5109,"useragent":5125,"utils":5126,"window-ext":5133}],5133:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":5134}],5134:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":5135,"js-ext/lib/object.js":5136}],5135:[function(require,module,exports){
module.exports=require(4)
},{}],5136:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5135,"polyfill/polyfill-base.js":5139,"utils":5140}],5137:[function(require,module,exports){
module.exports=require(14)
},{}],5138:[function(require,module,exports){
module.exports=require(15)
},{}],5139:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5137,"./lib/window.console.js":5138}],5140:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5141,"./lib/timers.js":5142}],5141:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5135,"polyfill/polyfill-base.js":5145}],5142:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5145}],5143:[function(require,module,exports){
module.exports=require(14)
},{}],5144:[function(require,module,exports){
module.exports=require(15)
},{}],5145:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5143,"./lib/window.console.js":5144}],5146:[function(require,module,exports){
arguments[4][1927][0].apply(exports,arguments)
},{"./css/panel.css":3483,"drag":3485,"event-mobile":3853,"focusmanager":3957,"js-ext/extra/hashmap.js":4265,"js-ext/extra/lightmap.js":4266,"js-ext/lib/object.js":4268,"js-ext/lib/string.js":4269,"node-plugin":4280,"polyfill":4452,"scrollable":5132,"window-ext":5133}],5147:[function(require,module,exports){
module.exports=require(14)
},{}],5148:[function(require,module,exports){
(function (global){
/*
* Copyright 2012 The Polymer Authors. All rights reserved.
* Use of this source code is goverened by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(global) {
"use strict";
var registrationsTable = new global.WeakMap(),
uidCounter = 0,
currentRecord, recordWithOldValue,
setImmediate, setImmediateQueue, sentinel, queue, isScheduled, scheduledObservers;
// As much as we would like to use the native implementation, IE
// (all versions) suffers a rather annoying bug where it will drop or defer
// callbacks when heavy DOM operations are being performed concurrently.
//
// For a thorough discussion on this, see:
// http://codeforhire.com/2013/09/21/setimmediate-and-messagechannel-broken-on-internet-explorer-10/
if (/Trident/.test(global.navigator.userAgent)) {
// Sadly, this bug also affects postMessage and MessageQueues.
//
// We would like to use the onreadystatechange hack for IE <= 10, but it is
// dangerous in the polyfilled environment due to requiring that the
// observed script element be in the document.
setImmediate = setTimeout;
// If some other browser ever implements it, let's prefer their native
// implementation:
} else if (global.setImmediate) {
setImmediate = global.setImmediate;
} else {
// Otherwise, we fall back to postMessage as a means of emulating the next
// task semantics of setImmediate.
setImmediateQueue = [];
sentinel = String(Math.random());
global.addEventListener('message', function(e) {
if (e.data === sentinel) {
queue = setImmediateQueue;
setImmediateQueue = [];
queue.forEach(function(func) {
func();
});
}
});
setImmediate = function(func) {
setImmediateQueue.push(func);
global.postMessage(sentinel, '*');
};
}
// This is used to ensure that we never schedule 2 callas to setImmediate
isScheduled = false;
// Keep track of observers that needs to be notified next time.
scheduledObservers = [];
/**
* Schedules |dispatchCallback| to be called in the future.
* @param {MutationObserver} observer
*/
function scheduleCallback(observer) {
scheduledObservers.push(observer);
if (!isScheduled) {
isScheduled = true;
setImmediate(dispatchCallbacks);
}
}
function wrapIfNeeded(node) {
return (global.ShadowDOMPolyfill && global.ShadowDOMPolyfill.wrapIfNeeded(node)) || node;
}
function dispatchCallbacks() {
// http://dom.spec.whatwg.org/#mutation-observers
var observers = scheduledObservers,
anyNonEmpty = false;
isScheduled = false; // Used to allow a new setImmediate call above.
scheduledObservers = [];
// Sort observers based on their creation UID (incremental).
observers.sort(function(o1, o2) {
return o1.uid_ - o2.uid_;
});
observers.forEach(function(observer) {
// 2.1, 2.2
var queue = observer.takeRecords();
// 2.3. Remove all transient registered observers whose observer is mo.
removeTransientObserversFor(observer);
// 2.4
if (queue.length) {
observer.callback_(queue, observer);
anyNonEmpty = true;
}
});
// 3.
if (anyNonEmpty) {
dispatchCallbacks();
}
}
function removeTransientObserversFor(observer) {
observer.nodes_.forEach(function(node) {
var registrations = registrationsTable.get(node);
if (!registrations) {
return;
}
registrations.forEach(function(registration) {
if (registration.observer === observer) {
registration.removeTransientObservers();
}
});
});
}
/**
* This function is used for the "For each registered observer observer (with
* observer's options as options) in target's list of registered observers,
* run these substeps:" and the "For each ancestor ancestor of target, and for
* each registered observer observer (with options options) in ancestor's list
* of registered observers, run these substeps:" part of the algorithms. The
* |options.subtree| is checked to ensure that the callback is called
* correctly.
*
* @param {Node} target
* @param {function(MutationObserverInit):MutationRecord} callback
*/
function forEachAncestorAndObserverEnqueueRecord(target, callback) {
var node, registration, registrations, j, options, record;
for (node = target; node; node = node.parentNode) {
registrations = registrationsTable.get(node);
if (registrations) {
for (j = 0; j < registrations.length; j++) {
registration = registrations[j];
options = registration.options;
// Only target ignores subtree.
if ((node !== target) && !options.subtree) {
continue;
}
record = callback(options);
if (record) {
registration.enqueue(record);
}
}
}
}
}
/**
* The class that maps to the DOM MutationObserver interface.
* @param {Function} callback.
* @constructor
*/
function JsMutationObserver(callback) {
var instance = this;
instance.callback_ = callback;
instance.nodes_ = [];
instance.records_ = [];
instance.uid_ = ++uidCounter;
}
JsMutationObserver.prototype = {
observe: function(target, options) {
var registrations, registration, i;
target = wrapIfNeeded(target);
if (
// 1.1
(!options.childList && !options.attributes && !options.characterData) ||
// 1.2
(options.attributeOldValue && !options.attributes) ||
// 1.3
(options.attributeFilter && options.attributeFilter.length && !options.attributes) ||
// 1.4
(options.characterDataOldValue && !options.characterData)) {
throw new SyntaxError();
}
registrations = registrationsTable.get(target);
if (!registrations) {
registrationsTable.set(target, registrations = []);
}
// 2
// If target's list of registered observers already includes a registered
// observer associated with the context object, replace that registered
// observer's options with options.
for (i = 0; i < registrations.length; i++) {
if (registrations[i].observer === this) {
registration = registrations[i];
registration.removeListeners();
registration.options = options;
break;
}
}
// 3.
// Otherwise, add a new registered observer to target's list of registered
// observers with the context object as the observer and options as the
// options, and add target to context object's list of nodes on which it
// is registered.
if (!registration) {
registration = new Registration(this, target, options);
registrations.push(registration);
this.nodes_.push(target);
}
registration.addListeners();
},
disconnect: function() {
var instance = this,
len = instance.nodes_.length,
i, k, node, registrations, registration;
for (k=0; k<len; k++) {
node = instance.nodes_[k];
registrations = registrationsTable.get(node);
for (i = 0; i < registrations.length; i++) {
registration = registrations[i];
if (registration.observer === instance) {
registration.removeListeners();
registrations.splice(i, 1);
// Each node can only have one registered observer associated with
// this observer.
break;
}
}
}
instance.records_ = [];
},
takeRecords: function() {
var copyOfRecords = this.records_;
this.records_ = [];
return copyOfRecords;
}
};
/**
* @param {string} type
* @param {Node} target
* @constructor
*/
function MutationRecord(type, target) {
var instance = this;
instance.type = type;
instance.target = target;
instance.addedNodes = [];
instance.removedNodes = [];
instance.previousSibling = null;
instance.nextSibling = null;
instance.attributeName = null;
instance.attributeNamespace = null;
instance.oldValue = null;
}
function copyMutationRecord(original) {
var record = new MutationRecord(original.type, original.target);
record.addedNodes = original.addedNodes.slice();
record.removedNodes = original.removedNodes.slice();
record.previousSibling = original.previousSibling;
record.nextSibling = original.nextSibling;
record.attributeName = original.attributeName;
record.attributeNamespace = original.attributeNamespace;
record.oldValue = original.oldValue;
return record;
}
// We keep track of the two (possibly one) records used in a single mutation.
/**
* Creates a record without |oldValue| and caches it as |currentRecord| for
* later use.
* @param {string} oldValue
* @return {MutationRecord}
*/
function getRecord(type, target) {
currentRecord = new MutationRecord(type, target);
return currentRecord;
}
/**
* Gets or creates a record with |oldValue| based in the |currentRecord|
* @param {string} oldValue
* @return {MutationRecord}
*/
function getRecordWithOldValue(oldValue) {
if (recordWithOldValue) {
return recordWithOldValue;
}
recordWithOldValue = copyMutationRecord(currentRecord);
recordWithOldValue.oldValue = oldValue;
return recordWithOldValue;
}
function clearRecords() {
currentRecord = recordWithOldValue = undefined;
}
/**
* @param {MutationRecord} record
* @return {boolean} Whether the record represents a record from the current
* mutation event.
*/
function recordRepresentsCurrentMutation(record) {
return (record === recordWithOldValue) || (record === currentRecord);
}
/**
* Selects which record, if any, to replace the last record in the queue.
* This returns |null| if no record should be replaced.
*
* @param {MutationRecord} lastRecord
* @param {MutationRecord} newRecord
* @param {MutationRecord}
*/
function selectRecord(lastRecord, newRecord) {
if (lastRecord === newRecord) {
return lastRecord;
}
// Check if the the record we are adding represents the same record. If
// so, we keep the one with the oldValue in it.
if (recordWithOldValue && recordRepresentsCurrentMutation(lastRecord)) {
return recordWithOldValue;
}
return null;
}
/**
* Class used to represent a registered observer.
* @param {MutationObserver} observer
* @param {Node} target
* @param {MutationObserverInit} options
* @constructor
*/
function Registration(observer, target, options) {
var instance = this;
instance.observer = observer;
instance.target = target;
instance.options = options;
instance.transientObservedNodes = [];
}
Registration.prototype = {
enqueue: function(record) {
var records = this.observer.records_,
length = records.length,
lastRecord, recordToReplaceLast;
// There are cases where we replace the last record with the new record.
// For example if the record represents the same mutation we need to use
// the one with the oldValue. If we get same record (this can happen as we
// walk up the tree) we ignore the new record.
if (records.length > 0) {
lastRecord = records[length - 1];
recordToReplaceLast = selectRecord(lastRecord, record);
if (recordToReplaceLast) {
records[length - 1] = recordToReplaceLast;
return;
}
} else {
scheduleCallback(this.observer);
}
records[length] = record;
},
changeListeners_: function(node, remove) {
var instance = this,
options = instance.options;
if (options.attributes) {
remove ? node.removeEventListener('DOMAttrModified', instance, true) : node.addEventListener('DOMAttrModified', instance, true);
}
if (options.characterData) {
remove ? node.removeEventListener('DOMCharacterDataModified', instance, true) : node.addEventListener('DOMCharacterDataModified', instance, true);
}
if (options.childList) {
remove ? node.removeEventListener('DOMNodeInserted', instance, true) : node.addEventListener('DOMNodeInserted', instance, true);
}
if (options.childList || options.subtree) {
remove ? node.removeEventListener('DOMNodeRemoved', instance, true) : node.addEventListener('DOMNodeRemoved', instance, true);
}
},
addListeners: function() {
this.changeListeners_(this.target);
},
removeListeners: function() {
this.changeListeners_(this.target, true);
},
/**
* Adds a transient observer on node. The transient observer gets removed
* next time we deliver the change records.
* @param {Node} node
*/
addTransientObserver: function(node) {
var instance = this,
registrations;
// Don't add transient observers on the target itself. We already have all
// the required listeners set up on the target.
if (node === instance.target) {
return;
}
instance.changeListeners_(node);
instance.transientObservedNodes.push(node);
registrations = registrationsTable.get(node);
if (!registrations) {
registrationsTable.set(node, registrations = []);
}
// We know that registrations does not contain this because we already
// checked if node === this.target.
registrations.push(instance);
},
removeTransientObservers: function() {
var instance = this,
transientObservedNodes = instance.transientObservedNodes,
len = transientObservedNodes.length,
registrations, i, k, node;
instance.transientObservedNodes = [];
for (k=0; k<len; k++) {
node = transientObservedNodes[k];
// Transient observers are never added to the target.
instance.changeListeners_(node, true);
registrations = registrationsTable.get(node);
for (i = 0; i < registrations.length; i++) {
if (registrations[i] === instance) {
registrations.splice(i, 1);
// Each node can only have one registered observer associated with
// this observer.
break;
}
}
}
},
handleEvent: function(e) {
var name, namespace, target, record, changedNode, addedNodes, removedNodes, previousSibling, nextSibling, oldValue;
// Stop propagation since we are managing the propagation manually.
// This means that other mutation events on the page will not work
// correctly but that is by design.
e.stopImmediatePropagation();
switch (e.type) {
case 'DOMAttrModified':
// http://dom.spec.whatwg.org/#concept-mo-queue-attributes
name = e.attrName;
namespace = e.relatedNode.namespaceURI;
target = e.target;
// 1.
record = getRecord('attributes', target);
record.attributeName = name;
record.attributeNamespace = namespace;
// 2.
oldValue = (e.attrChange === global.MutationEvent.ADDITION) ? null : e.prevValue;
forEachAncestorAndObserverEnqueueRecord(target, function(options) {
// 3.1, 4.2
if (!options.attributes) {
return;
}
// 3.2, 4.3
if (options.attributeFilter && options.attributeFilter.length &&
(options.attributeFilter.indexOf(name) === -1) &&
(options.attributeFilter.indexOf(namespace) === -1)) {
return;
}
// 3.3, 4.4
if (options.attributeOldValue) {
return getRecordWithOldValue(oldValue);
}
// 3.4, 4.5
return record;
});
break;
case 'DOMCharacterDataModified':
// http://dom.spec.whatwg.org/#concept-mo-queue-characterdata
target = e.target;
// 1.
record = getRecord('characterData', target);
// 2.
oldValue = e.prevValue;
forEachAncestorAndObserverEnqueueRecord(target, function(options) {
// 3.1, 4.2
if (!options.characterData) {
return;
}
// 3.2, 4.3
if (options.characterDataOldValue) {
return getRecordWithOldValue(oldValue);
}
// 3.3, 4.4
return record;
});
break;
case 'DOMNodeRemoved':
this.addTransientObserver(e.target);
// Fall through.
/* falls through */
case 'DOMNodeInserted':
// http://dom.spec.whatwg.org/#concept-mo-queue-childlist
target = e.relatedNode;
changedNode = e.target;
if (e.type === 'DOMNodeInserted') {
addedNodes = [changedNode];
removedNodes = [];
} else {
addedNodes = [];
removedNodes = [changedNode];
}
previousSibling = changedNode.previousSibling;
nextSibling = changedNode.nextSibling;
// 1.
record = getRecord('childList', target);
record.addedNodes = addedNodes;
record.removedNodes = removedNodes;
record.previousSibling = previousSibling;
record.nextSibling = nextSibling;
forEachAncestorAndObserverEnqueueRecord(target, function(options) {
// 2.1, 3.2
if (!options.childList) {
return;
}
// 2.2, 3.3
return record;
});
}
clearRecords();
}
};
global.JsMutationObserver = JsMutationObserver;
if (!global.MutationObserver) {
global.MutationObserver = JsMutationObserver;
}
}(typeof global !== 'undefined' ? global : /* istanbul ignore next */ this));
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],5149:[function(require,module,exports){
require('ypromise');
},{"ypromise":5933}],5150:[function(require,module,exports){
module.exports=require(57)
},{}],5151:[function(require,module,exports){
module.exports=require(15)
},{}],5152:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5147,"./lib/window.console.js":5151}],5153:[function(require,module,exports){
require('./polyfill-base.js');
require('./lib/promise.js');
require('./lib/weakmap.js');
require('./lib/mutationobserver.js'); // needs weakmap
},{"./lib/mutationobserver.js":5148,"./lib/promise.js":5149,"./lib/weakmap.js":5150,"./polyfill-base.js":5152}],5154:[function(require,module,exports){
module.exports=require(1234)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],5155:[function(require,module,exports){
module.exports=require(265)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],5156:[function(require,module,exports){
module.exports=require(266)
},{"./css/drag.css":5155,"event-dom":5157,"js-ext":5253,"js-ext/extra/hashmap.js":5252,"node-plugin":5269,"polyfill":5441,"useragent":5457,"vdom":5510,"window-ext":5511}],5157:[function(require,module,exports){
module.exports=require(267)
},{"event":5161,"js-ext/extra/hashmap.js":5177,"js-ext/lib/array.js":5178,"js-ext/lib/object.js":5179,"js-ext/lib/string.js":5180,"polyfill/polyfill-base.js":5192,"utils":5193,"vdom":5251}],5158:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":5163,"js-ext/lib/object.js":5164,"polyfill/polyfill-base.js":5176}],5159:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":5158}],5160:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":5158,"js-ext/extra/classes.js":5162,"js-ext/lib/object.js":5164}],5161:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":5158,"./event-emitter.js":5159,"./event-listener.js":5160}],5162:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":5164,"js-ext/extra/hashmap.js":5163,"polyfill/polyfill-base.js":5167}],5163:[function(require,module,exports){
module.exports=require(4)
},{}],5164:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5163,"polyfill/polyfill-base.js":5167,"utils":5168}],5165:[function(require,module,exports){
module.exports=require(14)
},{}],5166:[function(require,module,exports){
module.exports=require(15)
},{}],5167:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5165,"./lib/window.console.js":5166}],5168:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5169,"./lib/timers.js":5170}],5169:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5163,"polyfill/polyfill-base.js":5173}],5170:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5173}],5171:[function(require,module,exports){
module.exports=require(14)
},{}],5172:[function(require,module,exports){
module.exports=require(15)
},{}],5173:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5171,"./lib/window.console.js":5172}],5174:[function(require,module,exports){
module.exports=require(14)
},{}],5175:[function(require,module,exports){
module.exports=require(15)
},{}],5176:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5174,"./lib/window.console.js":5175}],5177:[function(require,module,exports){
module.exports=require(4)
},{}],5178:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":5183}],5179:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5177,"polyfill/polyfill-base.js":5183,"utils":5184}],5180:[function(require,module,exports){
module.exports=require(29)
},{}],5181:[function(require,module,exports){
module.exports=require(14)
},{}],5182:[function(require,module,exports){
module.exports=require(15)
},{}],5183:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5181,"./lib/window.console.js":5182}],5184:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5185,"./lib/timers.js":5186}],5185:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5177,"polyfill/polyfill-base.js":5189}],5186:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5189}],5187:[function(require,module,exports){
module.exports=require(14)
},{}],5188:[function(require,module,exports){
module.exports=require(15)
},{}],5189:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5187,"./lib/window.console.js":5188}],5190:[function(require,module,exports){
module.exports=require(14)
},{}],5191:[function(require,module,exports){
module.exports=require(15)
},{}],5192:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5190,"./lib/window.console.js":5191}],5193:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5194,"./lib/timers.js":5195}],5194:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5177,"polyfill/polyfill-base.js":5198}],5195:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5198}],5196:[function(require,module,exports){
module.exports=require(14)
},{}],5197:[function(require,module,exports){
module.exports=require(15)
},{}],5198:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5196,"./lib/window.console.js":5197}],5199:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],5200:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":5204,"js-ext/extra/hashmap.js":5201,"polyfill/polyfill-base.js":5210}],5201:[function(require,module,exports){
module.exports=require(4)
},{}],5202:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":5203,"../lib/object.js":5204,"./classes.js":5200,"js-ext/extra/hashmap.js":5201,"polyfill/lib/weakmap.js":5208}],5203:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":5210}],5204:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5201,"polyfill/polyfill-base.js":5210,"utils":5211}],5205:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":5210}],5206:[function(require,module,exports){
module.exports=require(29)
},{}],5207:[function(require,module,exports){
module.exports=require(14)
},{}],5208:[function(require,module,exports){
module.exports=require(57)
},{}],5209:[function(require,module,exports){
module.exports=require(15)
},{}],5210:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5207,"./lib/window.console.js":5209}],5211:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5212,"./lib/timers.js":5213}],5212:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5201,"polyfill/polyfill-base.js":5216}],5213:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5216}],5214:[function(require,module,exports){
module.exports=require(14)
},{}],5215:[function(require,module,exports){
module.exports=require(15)
},{}],5216:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5214,"./lib/window.console.js":5215}],5217:[function(require,module,exports){
module.exports=require(66)
},{}],5218:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":5217}],5219:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":5217}],5220:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":5217}],5221:[function(require,module,exports){
module.exports=require(14)
},{}],5222:[function(require,module,exports){
module.exports=require(15)
},{}],5223:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5221,"./lib/window.console.js":5222}],5224:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5225,"./lib/timers.js":5226}],5225:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5201,"polyfill/polyfill-base.js":5229}],5226:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5229}],5227:[function(require,module,exports){
module.exports=require(14)
},{}],5228:[function(require,module,exports){
module.exports=require(15)
},{}],5229:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5227,"./lib/window.console.js":5228}],5230:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":5231}],5231:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":5232,"js-ext/lib/object.js":5233}],5232:[function(require,module,exports){
module.exports=require(4)
},{}],5233:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5232,"polyfill/polyfill-base.js":5236,"utils":5237}],5234:[function(require,module,exports){
module.exports=require(14)
},{}],5235:[function(require,module,exports){
module.exports=require(15)
},{}],5236:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5234,"./lib/window.console.js":5235}],5237:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5238,"./lib/timers.js":5239}],5238:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5232,"polyfill/polyfill-base.js":5242}],5239:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5242}],5240:[function(require,module,exports){
module.exports=require(14)
},{}],5241:[function(require,module,exports){
module.exports=require(15)
},{}],5242:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5240,"./lib/window.console.js":5241}],5243:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":5201,"js-ext/lib/object.js":5204,"js-ext/lib/string.js":5206,"polyfill":5223,"polyfill/extra/transition.js":5218,"polyfill/extra/vendorCSS.js":5220}],5244:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":5201,"js-ext/lib/object.js":5204,"polyfill":5223}],5245:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":5201,"js-ext/lib/object.js":5204,"js-ext/lib/string.js":5206,"polyfill":5223}],5246:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":5199,"./attribute-extractor.js":5243,"./element-array.js":5244,"./html-parser.js":5247,"./node-parser.js":5248,"./vdom-ns.js":5249,"./vnode.js":5250,"js-ext/extra/hashmap.js":5201,"js-ext/lib/object.js":5204,"js-ext/lib/promise.js":5205,"js-ext/lib/string.js":5206,"polyfill":5223,"polyfill/extra/transition.js":5218,"polyfill/extra/transitionend.js":5219,"polyfill/extra/vendorCSS.js":5220,"utils":5224,"window-ext":5230}],5247:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":5243,"./vdom-ns.js":5249,"js-ext/extra/hashmap.js":5201,"js-ext/lib/object.js":5204,"polyfill":5223}],5248:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":5243,"./vdom-ns.js":5249,"./vnode.js":5250,"js-ext/extra/hashmap.js":5201,"js-ext/lib/object.js":5204,"polyfill":5223}],5249:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":5201,"js-ext/lib/object.js":5204,"polyfill":5223}],5250:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":5243,"./html-parser.js":5247,"./vdom-ns.js":5249,"js-ext/extra/hashmap.js":5201,"js-ext/extra/lightmap.js":5202,"js-ext/lib/array.js":5203,"js-ext/lib/object.js":5204,"js-ext/lib/string.js":5206,"polyfill":5223,"utils/lib/timers.js":5226}],5251:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":5245,"./partials/extend-element.js":5246,"./partials/node-parser.js":5248,"js-ext/extra/hashmap.js":5201,"js-ext/lib/object.js":5204,"utils/lib/timers.js":5226}],5252:[function(require,module,exports){
module.exports=require(4)
},{}],5253:[function(require,module,exports){
module.exports=require(202)
},{"./lib/array.js":5254,"./lib/function.js":5255,"./lib/json.js":5256,"./lib/object.js":5257,"./lib/promise.js":5258,"./lib/string.js":5259}],5254:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":5262}],5255:[function(require,module,exports){
module.exports=require(204)
},{"polyfill/polyfill-base.js":5262}],5256:[function(require,module,exports){
module.exports=require(205)
},{"polyfill/polyfill-base.js":5262}],5257:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5252,"polyfill/polyfill-base.js":5262,"utils":5263}],5258:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":5262}],5259:[function(require,module,exports){
module.exports=require(29)
},{}],5260:[function(require,module,exports){
module.exports=require(14)
},{}],5261:[function(require,module,exports){
module.exports=require(15)
},{}],5262:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5260,"./lib/window.console.js":5261}],5263:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5264,"./lib/timers.js":5265}],5264:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5252,"polyfill/polyfill-base.js":5268}],5265:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5268}],5266:[function(require,module,exports){
module.exports=require(14)
},{}],5267:[function(require,module,exports){
module.exports=require(15)
},{}],5268:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5266,"./lib/window.console.js":5267}],5269:[function(require,module,exports){
arguments[4][5][0].apply(exports,arguments)
},{"event-dom":5270,"js-ext/extra/classes.js":5365,"js-ext/extra/hashmap.js":5366,"js-ext/lib/object.js":5367,"js-ext/lib/promise.js":5368,"js-ext/lib/string.js":5369,"polyfill":5381,"utils/lib/timers.js":5382,"vdom":5438}],5270:[function(require,module,exports){
module.exports=require(267)
},{"event":5274,"js-ext/extra/hashmap.js":5290,"js-ext/lib/array.js":5291,"js-ext/lib/object.js":5292,"js-ext/lib/string.js":5293,"polyfill/polyfill-base.js":5305,"utils":5306,"vdom":5364}],5271:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":5276,"js-ext/lib/object.js":5277,"polyfill/polyfill-base.js":5289}],5272:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":5271}],5273:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":5271,"js-ext/extra/classes.js":5275,"js-ext/lib/object.js":5277}],5274:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":5271,"./event-emitter.js":5272,"./event-listener.js":5273}],5275:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":5277,"js-ext/extra/hashmap.js":5276,"polyfill/polyfill-base.js":5280}],5276:[function(require,module,exports){
module.exports=require(4)
},{}],5277:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5276,"polyfill/polyfill-base.js":5280,"utils":5281}],5278:[function(require,module,exports){
module.exports=require(14)
},{}],5279:[function(require,module,exports){
module.exports=require(15)
},{}],5280:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5278,"./lib/window.console.js":5279}],5281:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5282,"./lib/timers.js":5283}],5282:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5276,"polyfill/polyfill-base.js":5286}],5283:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5286}],5284:[function(require,module,exports){
module.exports=require(14)
},{}],5285:[function(require,module,exports){
module.exports=require(15)
},{}],5286:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5284,"./lib/window.console.js":5285}],5287:[function(require,module,exports){
module.exports=require(14)
},{}],5288:[function(require,module,exports){
module.exports=require(15)
},{}],5289:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5287,"./lib/window.console.js":5288}],5290:[function(require,module,exports){
module.exports=require(4)
},{}],5291:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":5296}],5292:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5290,"polyfill/polyfill-base.js":5296,"utils":5297}],5293:[function(require,module,exports){
module.exports=require(29)
},{}],5294:[function(require,module,exports){
module.exports=require(14)
},{}],5295:[function(require,module,exports){
module.exports=require(15)
},{}],5296:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5294,"./lib/window.console.js":5295}],5297:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5298,"./lib/timers.js":5299}],5298:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5290,"polyfill/polyfill-base.js":5302}],5299:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5302}],5300:[function(require,module,exports){
module.exports=require(14)
},{}],5301:[function(require,module,exports){
module.exports=require(15)
},{}],5302:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5300,"./lib/window.console.js":5301}],5303:[function(require,module,exports){
module.exports=require(14)
},{}],5304:[function(require,module,exports){
module.exports=require(15)
},{}],5305:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5303,"./lib/window.console.js":5304}],5306:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5307,"./lib/timers.js":5308}],5307:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5290,"polyfill/polyfill-base.js":5311}],5308:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5311}],5309:[function(require,module,exports){
module.exports=require(14)
},{}],5310:[function(require,module,exports){
module.exports=require(15)
},{}],5311:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5309,"./lib/window.console.js":5310}],5312:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],5313:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":5317,"js-ext/extra/hashmap.js":5314,"polyfill/polyfill-base.js":5323}],5314:[function(require,module,exports){
module.exports=require(4)
},{}],5315:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":5316,"../lib/object.js":5317,"./classes.js":5313,"js-ext/extra/hashmap.js":5314,"polyfill/lib/weakmap.js":5321}],5316:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":5323}],5317:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5314,"polyfill/polyfill-base.js":5323,"utils":5324}],5318:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":5323}],5319:[function(require,module,exports){
module.exports=require(29)
},{}],5320:[function(require,module,exports){
module.exports=require(14)
},{}],5321:[function(require,module,exports){
module.exports=require(57)
},{}],5322:[function(require,module,exports){
module.exports=require(15)
},{}],5323:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5320,"./lib/window.console.js":5322}],5324:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5325,"./lib/timers.js":5326}],5325:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5314,"polyfill/polyfill-base.js":5329}],5326:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5329}],5327:[function(require,module,exports){
module.exports=require(14)
},{}],5328:[function(require,module,exports){
module.exports=require(15)
},{}],5329:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5327,"./lib/window.console.js":5328}],5330:[function(require,module,exports){
module.exports=require(66)
},{}],5331:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":5330}],5332:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":5330}],5333:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":5330}],5334:[function(require,module,exports){
module.exports=require(14)
},{}],5335:[function(require,module,exports){
module.exports=require(15)
},{}],5336:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5334,"./lib/window.console.js":5335}],5337:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5338,"./lib/timers.js":5339}],5338:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5314,"polyfill/polyfill-base.js":5342}],5339:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5342}],5340:[function(require,module,exports){
module.exports=require(14)
},{}],5341:[function(require,module,exports){
module.exports=require(15)
},{}],5342:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5340,"./lib/window.console.js":5341}],5343:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":5344}],5344:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":5345,"js-ext/lib/object.js":5346}],5345:[function(require,module,exports){
module.exports=require(4)
},{}],5346:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5345,"polyfill/polyfill-base.js":5349,"utils":5350}],5347:[function(require,module,exports){
module.exports=require(14)
},{}],5348:[function(require,module,exports){
module.exports=require(15)
},{}],5349:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5347,"./lib/window.console.js":5348}],5350:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5351,"./lib/timers.js":5352}],5351:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5345,"polyfill/polyfill-base.js":5355}],5352:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5355}],5353:[function(require,module,exports){
module.exports=require(14)
},{}],5354:[function(require,module,exports){
module.exports=require(15)
},{}],5355:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5353,"./lib/window.console.js":5354}],5356:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":5314,"js-ext/lib/object.js":5317,"js-ext/lib/string.js":5319,"polyfill":5336,"polyfill/extra/transition.js":5331,"polyfill/extra/vendorCSS.js":5333}],5357:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":5314,"js-ext/lib/object.js":5317,"polyfill":5336}],5358:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":5314,"js-ext/lib/object.js":5317,"js-ext/lib/string.js":5319,"polyfill":5336}],5359:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":5312,"./attribute-extractor.js":5356,"./element-array.js":5357,"./html-parser.js":5360,"./node-parser.js":5361,"./vdom-ns.js":5362,"./vnode.js":5363,"js-ext/extra/hashmap.js":5314,"js-ext/lib/object.js":5317,"js-ext/lib/promise.js":5318,"js-ext/lib/string.js":5319,"polyfill":5336,"polyfill/extra/transition.js":5331,"polyfill/extra/transitionend.js":5332,"polyfill/extra/vendorCSS.js":5333,"utils":5337,"window-ext":5343}],5360:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":5356,"./vdom-ns.js":5362,"js-ext/extra/hashmap.js":5314,"js-ext/lib/object.js":5317,"polyfill":5336}],5361:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":5356,"./vdom-ns.js":5362,"./vnode.js":5363,"js-ext/extra/hashmap.js":5314,"js-ext/lib/object.js":5317,"polyfill":5336}],5362:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":5314,"js-ext/lib/object.js":5317,"polyfill":5336}],5363:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":5356,"./html-parser.js":5360,"./vdom-ns.js":5362,"js-ext/extra/hashmap.js":5314,"js-ext/extra/lightmap.js":5315,"js-ext/lib/array.js":5316,"js-ext/lib/object.js":5317,"js-ext/lib/string.js":5319,"polyfill":5336,"utils/lib/timers.js":5339}],5364:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":5358,"./partials/extend-element.js":5359,"./partials/node-parser.js":5361,"js-ext/extra/hashmap.js":5314,"js-ext/lib/object.js":5317,"utils/lib/timers.js":5339}],5365:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":5367,"js-ext/extra/hashmap.js":5366,"polyfill/polyfill-base.js":5372}],5366:[function(require,module,exports){
module.exports=require(4)
},{}],5367:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5366,"polyfill/polyfill-base.js":5372,"utils":5373}],5368:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":5372}],5369:[function(require,module,exports){
module.exports=require(29)
},{}],5370:[function(require,module,exports){
module.exports=require(14)
},{}],5371:[function(require,module,exports){
module.exports=require(15)
},{}],5372:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5370,"./lib/window.console.js":5371}],5373:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5374,"./lib/timers.js":5375}],5374:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5366,"polyfill/polyfill-base.js":5378}],5375:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5378}],5376:[function(require,module,exports){
module.exports=require(14)
},{}],5377:[function(require,module,exports){
module.exports=require(15)
},{}],5378:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5376,"./lib/window.console.js":5377}],5379:[function(require,module,exports){
module.exports=require(14)
},{}],5380:[function(require,module,exports){
module.exports=require(15)
},{}],5381:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5379,"./lib/window.console.js":5380}],5382:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5385}],5383:[function(require,module,exports){
module.exports=require(14)
},{}],5384:[function(require,module,exports){
module.exports=require(15)
},{}],5385:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5383,"./lib/window.console.js":5384}],5386:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],5387:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":5391,"js-ext/extra/hashmap.js":5388,"polyfill/polyfill-base.js":5397}],5388:[function(require,module,exports){
module.exports=require(4)
},{}],5389:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":5390,"../lib/object.js":5391,"./classes.js":5387,"js-ext/extra/hashmap.js":5388,"polyfill/lib/weakmap.js":5395}],5390:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":5397}],5391:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5388,"polyfill/polyfill-base.js":5397,"utils":5398}],5392:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":5397}],5393:[function(require,module,exports){
module.exports=require(29)
},{}],5394:[function(require,module,exports){
module.exports=require(14)
},{}],5395:[function(require,module,exports){
module.exports=require(57)
},{}],5396:[function(require,module,exports){
module.exports=require(15)
},{}],5397:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5394,"./lib/window.console.js":5396}],5398:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5399,"./lib/timers.js":5400}],5399:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5388,"polyfill/polyfill-base.js":5403}],5400:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5403}],5401:[function(require,module,exports){
module.exports=require(14)
},{}],5402:[function(require,module,exports){
module.exports=require(15)
},{}],5403:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5401,"./lib/window.console.js":5402}],5404:[function(require,module,exports){
module.exports=require(66)
},{}],5405:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":5404}],5406:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":5404}],5407:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":5404}],5408:[function(require,module,exports){
module.exports=require(14)
},{}],5409:[function(require,module,exports){
module.exports=require(15)
},{}],5410:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5408,"./lib/window.console.js":5409}],5411:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5412,"./lib/timers.js":5413}],5412:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5388,"polyfill/polyfill-base.js":5416}],5413:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5416}],5414:[function(require,module,exports){
module.exports=require(14)
},{}],5415:[function(require,module,exports){
module.exports=require(15)
},{}],5416:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5414,"./lib/window.console.js":5415}],5417:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":5418}],5418:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":5419,"js-ext/lib/object.js":5420}],5419:[function(require,module,exports){
module.exports=require(4)
},{}],5420:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5419,"polyfill/polyfill-base.js":5423,"utils":5424}],5421:[function(require,module,exports){
module.exports=require(14)
},{}],5422:[function(require,module,exports){
module.exports=require(15)
},{}],5423:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5421,"./lib/window.console.js":5422}],5424:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5425,"./lib/timers.js":5426}],5425:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5419,"polyfill/polyfill-base.js":5429}],5426:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5429}],5427:[function(require,module,exports){
module.exports=require(14)
},{}],5428:[function(require,module,exports){
module.exports=require(15)
},{}],5429:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5427,"./lib/window.console.js":5428}],5430:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":5388,"js-ext/lib/object.js":5391,"js-ext/lib/string.js":5393,"polyfill":5410,"polyfill/extra/transition.js":5405,"polyfill/extra/vendorCSS.js":5407}],5431:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":5388,"js-ext/lib/object.js":5391,"polyfill":5410}],5432:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":5388,"js-ext/lib/object.js":5391,"js-ext/lib/string.js":5393,"polyfill":5410}],5433:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":5386,"./attribute-extractor.js":5430,"./element-array.js":5431,"./html-parser.js":5434,"./node-parser.js":5435,"./vdom-ns.js":5436,"./vnode.js":5437,"js-ext/extra/hashmap.js":5388,"js-ext/lib/object.js":5391,"js-ext/lib/promise.js":5392,"js-ext/lib/string.js":5393,"polyfill":5410,"polyfill/extra/transition.js":5405,"polyfill/extra/transitionend.js":5406,"polyfill/extra/vendorCSS.js":5407,"utils":5411,"window-ext":5417}],5434:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":5430,"./vdom-ns.js":5436,"js-ext/extra/hashmap.js":5388,"js-ext/lib/object.js":5391,"polyfill":5410}],5435:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":5430,"./vdom-ns.js":5436,"./vnode.js":5437,"js-ext/extra/hashmap.js":5388,"js-ext/lib/object.js":5391,"polyfill":5410}],5436:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":5388,"js-ext/lib/object.js":5391,"polyfill":5410}],5437:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":5430,"./html-parser.js":5434,"./vdom-ns.js":5436,"js-ext/extra/hashmap.js":5388,"js-ext/extra/lightmap.js":5389,"js-ext/lib/array.js":5390,"js-ext/lib/object.js":5391,"js-ext/lib/string.js":5393,"polyfill":5410,"utils/lib/timers.js":5413}],5438:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":5432,"./partials/extend-element.js":5433,"./partials/node-parser.js":5435,"js-ext/extra/hashmap.js":5388,"js-ext/lib/object.js":5391,"utils/lib/timers.js":5413}],5439:[function(require,module,exports){
module.exports=require(14)
},{}],5440:[function(require,module,exports){
module.exports=require(15)
},{}],5441:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5439,"./lib/window.console.js":5440}],5442:[function(require,module,exports){
module.exports=require(4)
},{}],5443:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5442,"polyfill/polyfill-base.js":5447,"utils":5448}],5444:[function(require,module,exports){
module.exports=require(29)
},{}],5445:[function(require,module,exports){
module.exports=require(14)
},{}],5446:[function(require,module,exports){
module.exports=require(15)
},{}],5447:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5445,"./lib/window.console.js":5446}],5448:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5449,"./lib/timers.js":5450}],5449:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5442,"polyfill/polyfill-base.js":5453}],5450:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5453}],5451:[function(require,module,exports){
module.exports=require(14)
},{}],5452:[function(require,module,exports){
module.exports=require(15)
},{}],5453:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5451,"./lib/window.console.js":5452}],5454:[function(require,module,exports){
module.exports=require(14)
},{}],5455:[function(require,module,exports){
module.exports=require(15)
},{}],5456:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5454,"./lib/window.console.js":5455}],5457:[function(require,module,exports){
module.exports=require(567)
},{"js-ext/extra/hashmap.js":5442,"js-ext/lib/object.js":5443,"js-ext/lib/string.js":5444,"polyfill":5456}],5458:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],5459:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":5463,"js-ext/extra/hashmap.js":5460,"polyfill/polyfill-base.js":5469}],5460:[function(require,module,exports){
module.exports=require(4)
},{}],5461:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":5462,"../lib/object.js":5463,"./classes.js":5459,"js-ext/extra/hashmap.js":5460,"polyfill/lib/weakmap.js":5467}],5462:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":5469}],5463:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5460,"polyfill/polyfill-base.js":5469,"utils":5470}],5464:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":5469}],5465:[function(require,module,exports){
module.exports=require(29)
},{}],5466:[function(require,module,exports){
module.exports=require(14)
},{}],5467:[function(require,module,exports){
module.exports=require(57)
},{}],5468:[function(require,module,exports){
module.exports=require(15)
},{}],5469:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5466,"./lib/window.console.js":5468}],5470:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5471,"./lib/timers.js":5472}],5471:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5460,"polyfill/polyfill-base.js":5475}],5472:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5475}],5473:[function(require,module,exports){
module.exports=require(14)
},{}],5474:[function(require,module,exports){
module.exports=require(15)
},{}],5475:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5473,"./lib/window.console.js":5474}],5476:[function(require,module,exports){
module.exports=require(66)
},{}],5477:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":5476}],5478:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":5476}],5479:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":5476}],5480:[function(require,module,exports){
module.exports=require(14)
},{}],5481:[function(require,module,exports){
module.exports=require(15)
},{}],5482:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5480,"./lib/window.console.js":5481}],5483:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5484,"./lib/timers.js":5485}],5484:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5460,"polyfill/polyfill-base.js":5488}],5485:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5488}],5486:[function(require,module,exports){
module.exports=require(14)
},{}],5487:[function(require,module,exports){
module.exports=require(15)
},{}],5488:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5486,"./lib/window.console.js":5487}],5489:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":5490}],5490:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":5491,"js-ext/lib/object.js":5492}],5491:[function(require,module,exports){
module.exports=require(4)
},{}],5492:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5491,"polyfill/polyfill-base.js":5495,"utils":5496}],5493:[function(require,module,exports){
module.exports=require(14)
},{}],5494:[function(require,module,exports){
module.exports=require(15)
},{}],5495:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5493,"./lib/window.console.js":5494}],5496:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5497,"./lib/timers.js":5498}],5497:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5491,"polyfill/polyfill-base.js":5501}],5498:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5501}],5499:[function(require,module,exports){
module.exports=require(14)
},{}],5500:[function(require,module,exports){
module.exports=require(15)
},{}],5501:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5499,"./lib/window.console.js":5500}],5502:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":5460,"js-ext/lib/object.js":5463,"js-ext/lib/string.js":5465,"polyfill":5482,"polyfill/extra/transition.js":5477,"polyfill/extra/vendorCSS.js":5479}],5503:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":5460,"js-ext/lib/object.js":5463,"polyfill":5482}],5504:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":5460,"js-ext/lib/object.js":5463,"js-ext/lib/string.js":5465,"polyfill":5482}],5505:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":5458,"./attribute-extractor.js":5502,"./element-array.js":5503,"./html-parser.js":5506,"./node-parser.js":5507,"./vdom-ns.js":5508,"./vnode.js":5509,"js-ext/extra/hashmap.js":5460,"js-ext/lib/object.js":5463,"js-ext/lib/promise.js":5464,"js-ext/lib/string.js":5465,"polyfill":5482,"polyfill/extra/transition.js":5477,"polyfill/extra/transitionend.js":5478,"polyfill/extra/vendorCSS.js":5479,"utils":5483,"window-ext":5489}],5506:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":5502,"./vdom-ns.js":5508,"js-ext/extra/hashmap.js":5460,"js-ext/lib/object.js":5463,"polyfill":5482}],5507:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":5502,"./vdom-ns.js":5508,"./vnode.js":5509,"js-ext/extra/hashmap.js":5460,"js-ext/lib/object.js":5463,"polyfill":5482}],5508:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":5460,"js-ext/lib/object.js":5463,"polyfill":5482}],5509:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":5502,"./html-parser.js":5506,"./vdom-ns.js":5508,"js-ext/extra/hashmap.js":5460,"js-ext/extra/lightmap.js":5461,"js-ext/lib/array.js":5462,"js-ext/lib/object.js":5463,"js-ext/lib/string.js":5465,"polyfill":5482,"utils/lib/timers.js":5485}],5510:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":5504,"./partials/extend-element.js":5505,"./partials/node-parser.js":5507,"js-ext/extra/hashmap.js":5460,"js-ext/lib/object.js":5463,"utils/lib/timers.js":5485}],5511:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":5512}],5512:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":5513,"js-ext/lib/object.js":5514}],5513:[function(require,module,exports){
module.exports=require(4)
},{}],5514:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5513,"polyfill/polyfill-base.js":5517,"utils":5518}],5515:[function(require,module,exports){
module.exports=require(14)
},{}],5516:[function(require,module,exports){
module.exports=require(15)
},{}],5517:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5515,"./lib/window.console.js":5516}],5518:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5519,"./lib/timers.js":5520}],5519:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5513,"polyfill/polyfill-base.js":5523}],5520:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5523}],5521:[function(require,module,exports){
module.exports=require(14)
},{}],5522:[function(require,module,exports){
module.exports=require(15)
},{}],5523:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5521,"./lib/window.console.js":5522}],5524:[function(require,module,exports){
module.exports=require(634)
},{"./lib/hammer-2.0.4.js":5525,"event-dom":5526}],5525:[function(require,module,exports){
module.exports=require(635)
},{"utils":5621}],5526:[function(require,module,exports){
module.exports=require(267)
},{"event":5530,"js-ext/extra/hashmap.js":5546,"js-ext/lib/array.js":5547,"js-ext/lib/object.js":5548,"js-ext/lib/string.js":5549,"polyfill/polyfill-base.js":5561,"utils":5562,"vdom":5620}],5527:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":5532,"js-ext/lib/object.js":5533,"polyfill/polyfill-base.js":5545}],5528:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":5527}],5529:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":5527,"js-ext/extra/classes.js":5531,"js-ext/lib/object.js":5533}],5530:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":5527,"./event-emitter.js":5528,"./event-listener.js":5529}],5531:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":5533,"js-ext/extra/hashmap.js":5532,"polyfill/polyfill-base.js":5536}],5532:[function(require,module,exports){
module.exports=require(4)
},{}],5533:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5532,"polyfill/polyfill-base.js":5536,"utils":5537}],5534:[function(require,module,exports){
module.exports=require(14)
},{}],5535:[function(require,module,exports){
module.exports=require(15)
},{}],5536:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5534,"./lib/window.console.js":5535}],5537:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5538,"./lib/timers.js":5539}],5538:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5532,"polyfill/polyfill-base.js":5542}],5539:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5542}],5540:[function(require,module,exports){
module.exports=require(14)
},{}],5541:[function(require,module,exports){
module.exports=require(15)
},{}],5542:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5540,"./lib/window.console.js":5541}],5543:[function(require,module,exports){
module.exports=require(14)
},{}],5544:[function(require,module,exports){
module.exports=require(15)
},{}],5545:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5543,"./lib/window.console.js":5544}],5546:[function(require,module,exports){
module.exports=require(4)
},{}],5547:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":5552}],5548:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5546,"polyfill/polyfill-base.js":5552,"utils":5553}],5549:[function(require,module,exports){
module.exports=require(29)
},{}],5550:[function(require,module,exports){
module.exports=require(14)
},{}],5551:[function(require,module,exports){
module.exports=require(15)
},{}],5552:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5550,"./lib/window.console.js":5551}],5553:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5554,"./lib/timers.js":5555}],5554:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5546,"polyfill/polyfill-base.js":5558}],5555:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5558}],5556:[function(require,module,exports){
module.exports=require(14)
},{}],5557:[function(require,module,exports){
module.exports=require(15)
},{}],5558:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5556,"./lib/window.console.js":5557}],5559:[function(require,module,exports){
module.exports=require(14)
},{}],5560:[function(require,module,exports){
module.exports=require(15)
},{}],5561:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5559,"./lib/window.console.js":5560}],5562:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5563,"./lib/timers.js":5564}],5563:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5546,"polyfill/polyfill-base.js":5567}],5564:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5567}],5565:[function(require,module,exports){
module.exports=require(14)
},{}],5566:[function(require,module,exports){
module.exports=require(15)
},{}],5567:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5565,"./lib/window.console.js":5566}],5568:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],5569:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":5573,"js-ext/extra/hashmap.js":5570,"polyfill/polyfill-base.js":5579}],5570:[function(require,module,exports){
module.exports=require(4)
},{}],5571:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":5572,"../lib/object.js":5573,"./classes.js":5569,"js-ext/extra/hashmap.js":5570,"polyfill/lib/weakmap.js":5577}],5572:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":5579}],5573:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5570,"polyfill/polyfill-base.js":5579,"utils":5580}],5574:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":5579}],5575:[function(require,module,exports){
module.exports=require(29)
},{}],5576:[function(require,module,exports){
module.exports=require(14)
},{}],5577:[function(require,module,exports){
module.exports=require(57)
},{}],5578:[function(require,module,exports){
module.exports=require(15)
},{}],5579:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5576,"./lib/window.console.js":5578}],5580:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5581,"./lib/timers.js":5582}],5581:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5570,"polyfill/polyfill-base.js":5585}],5582:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5585}],5583:[function(require,module,exports){
module.exports=require(14)
},{}],5584:[function(require,module,exports){
module.exports=require(15)
},{}],5585:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5583,"./lib/window.console.js":5584}],5586:[function(require,module,exports){
module.exports=require(66)
},{}],5587:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":5586}],5588:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":5586}],5589:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":5586}],5590:[function(require,module,exports){
module.exports=require(14)
},{}],5591:[function(require,module,exports){
module.exports=require(15)
},{}],5592:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5590,"./lib/window.console.js":5591}],5593:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5594,"./lib/timers.js":5595}],5594:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5570,"polyfill/polyfill-base.js":5598}],5595:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5598}],5596:[function(require,module,exports){
module.exports=require(14)
},{}],5597:[function(require,module,exports){
module.exports=require(15)
},{}],5598:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5596,"./lib/window.console.js":5597}],5599:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":5600}],5600:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":5601,"js-ext/lib/object.js":5602}],5601:[function(require,module,exports){
module.exports=require(4)
},{}],5602:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5601,"polyfill/polyfill-base.js":5605,"utils":5606}],5603:[function(require,module,exports){
module.exports=require(14)
},{}],5604:[function(require,module,exports){
module.exports=require(15)
},{}],5605:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5603,"./lib/window.console.js":5604}],5606:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5607,"./lib/timers.js":5608}],5607:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5601,"polyfill/polyfill-base.js":5611}],5608:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5611}],5609:[function(require,module,exports){
module.exports=require(14)
},{}],5610:[function(require,module,exports){
module.exports=require(15)
},{}],5611:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5609,"./lib/window.console.js":5610}],5612:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":5570,"js-ext/lib/object.js":5573,"js-ext/lib/string.js":5575,"polyfill":5592,"polyfill/extra/transition.js":5587,"polyfill/extra/vendorCSS.js":5589}],5613:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":5570,"js-ext/lib/object.js":5573,"polyfill":5592}],5614:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":5570,"js-ext/lib/object.js":5573,"js-ext/lib/string.js":5575,"polyfill":5592}],5615:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":5568,"./attribute-extractor.js":5612,"./element-array.js":5613,"./html-parser.js":5616,"./node-parser.js":5617,"./vdom-ns.js":5618,"./vnode.js":5619,"js-ext/extra/hashmap.js":5570,"js-ext/lib/object.js":5573,"js-ext/lib/promise.js":5574,"js-ext/lib/string.js":5575,"polyfill":5592,"polyfill/extra/transition.js":5587,"polyfill/extra/transitionend.js":5588,"polyfill/extra/vendorCSS.js":5589,"utils":5593,"window-ext":5599}],5616:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":5612,"./vdom-ns.js":5618,"js-ext/extra/hashmap.js":5570,"js-ext/lib/object.js":5573,"polyfill":5592}],5617:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":5612,"./vdom-ns.js":5618,"./vnode.js":5619,"js-ext/extra/hashmap.js":5570,"js-ext/lib/object.js":5573,"polyfill":5592}],5618:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":5570,"js-ext/lib/object.js":5573,"polyfill":5592}],5619:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":5612,"./html-parser.js":5616,"./vdom-ns.js":5618,"js-ext/extra/hashmap.js":5570,"js-ext/extra/lightmap.js":5571,"js-ext/lib/array.js":5572,"js-ext/lib/object.js":5573,"js-ext/lib/string.js":5575,"polyfill":5592,"utils/lib/timers.js":5595}],5620:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":5614,"./partials/extend-element.js":5615,"./partials/node-parser.js":5617,"js-ext/extra/hashmap.js":5570,"js-ext/lib/object.js":5573,"utils/lib/timers.js":5595}],5621:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5622,"./lib/timers.js":5623}],5622:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5627,"polyfill/polyfill-base.js":5626}],5623:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5626}],5624:[function(require,module,exports){
module.exports=require(14)
},{}],5625:[function(require,module,exports){
module.exports=require(15)
},{}],5626:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5624,"./lib/window.console.js":5625}],5627:[function(require,module,exports){
module.exports=require(4)
},{}],5628:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5627,"polyfill/polyfill-base.js":5631,"utils":5632}],5629:[function(require,module,exports){
module.exports=require(14)
},{}],5630:[function(require,module,exports){
module.exports=require(15)
},{}],5631:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5629,"./lib/window.console.js":5630}],5632:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5633,"./lib/timers.js":5634}],5633:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5627,"polyfill/polyfill-base.js":5637}],5634:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5637}],5635:[function(require,module,exports){
module.exports=require(14)
},{}],5636:[function(require,module,exports){
module.exports=require(15)
},{}],5637:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5635,"./lib/window.console.js":5636}],5638:[function(require,module,exports){
arguments[4][5][0].apply(exports,arguments)
},{"event-dom":5639,"js-ext/extra/classes.js":5734,"js-ext/extra/hashmap.js":5735,"js-ext/lib/object.js":5736,"js-ext/lib/promise.js":5737,"js-ext/lib/string.js":5738,"polyfill":5750,"utils/lib/timers.js":5751,"vdom":5807}],5639:[function(require,module,exports){
module.exports=require(267)
},{"event":5643,"js-ext/extra/hashmap.js":5659,"js-ext/lib/array.js":5660,"js-ext/lib/object.js":5661,"js-ext/lib/string.js":5662,"polyfill/polyfill-base.js":5674,"utils":5675,"vdom":5733}],5640:[function(require,module,exports){
module.exports=require(7)
},{"js-ext/extra/hashmap.js":5645,"js-ext/lib/object.js":5646,"polyfill/polyfill-base.js":5658}],5641:[function(require,module,exports){
module.exports=require(8)
},{"./event-base.js":5640}],5642:[function(require,module,exports){
module.exports=require(9)
},{"./event-base.js":5640,"js-ext/extra/classes.js":5644,"js-ext/lib/object.js":5646}],5643:[function(require,module,exports){
module.exports=require(10)
},{"./event-base.js":5640,"./event-emitter.js":5641,"./event-listener.js":5642}],5644:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":5646,"js-ext/extra/hashmap.js":5645,"polyfill/polyfill-base.js":5649}],5645:[function(require,module,exports){
module.exports=require(4)
},{}],5646:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5645,"polyfill/polyfill-base.js":5649,"utils":5650}],5647:[function(require,module,exports){
module.exports=require(14)
},{}],5648:[function(require,module,exports){
module.exports=require(15)
},{}],5649:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5647,"./lib/window.console.js":5648}],5650:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5651,"./lib/timers.js":5652}],5651:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5645,"polyfill/polyfill-base.js":5655}],5652:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5655}],5653:[function(require,module,exports){
module.exports=require(14)
},{}],5654:[function(require,module,exports){
module.exports=require(15)
},{}],5655:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5653,"./lib/window.console.js":5654}],5656:[function(require,module,exports){
module.exports=require(14)
},{}],5657:[function(require,module,exports){
module.exports=require(15)
},{}],5658:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5656,"./lib/window.console.js":5657}],5659:[function(require,module,exports){
module.exports=require(4)
},{}],5660:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":5665}],5661:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5659,"polyfill/polyfill-base.js":5665,"utils":5666}],5662:[function(require,module,exports){
module.exports=require(29)
},{}],5663:[function(require,module,exports){
module.exports=require(14)
},{}],5664:[function(require,module,exports){
module.exports=require(15)
},{}],5665:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5663,"./lib/window.console.js":5664}],5666:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5667,"./lib/timers.js":5668}],5667:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5659,"polyfill/polyfill-base.js":5671}],5668:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5671}],5669:[function(require,module,exports){
module.exports=require(14)
},{}],5670:[function(require,module,exports){
module.exports=require(15)
},{}],5671:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5669,"./lib/window.console.js":5670}],5672:[function(require,module,exports){
module.exports=require(14)
},{}],5673:[function(require,module,exports){
module.exports=require(15)
},{}],5674:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5672,"./lib/window.console.js":5673}],5675:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5676,"./lib/timers.js":5677}],5676:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5659,"polyfill/polyfill-base.js":5680}],5677:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5680}],5678:[function(require,module,exports){
module.exports=require(14)
},{}],5679:[function(require,module,exports){
module.exports=require(15)
},{}],5680:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5678,"./lib/window.console.js":5679}],5681:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],5682:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":5686,"js-ext/extra/hashmap.js":5683,"polyfill/polyfill-base.js":5692}],5683:[function(require,module,exports){
module.exports=require(4)
},{}],5684:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":5685,"../lib/object.js":5686,"./classes.js":5682,"js-ext/extra/hashmap.js":5683,"polyfill/lib/weakmap.js":5690}],5685:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":5692}],5686:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5683,"polyfill/polyfill-base.js":5692,"utils":5693}],5687:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":5692}],5688:[function(require,module,exports){
module.exports=require(29)
},{}],5689:[function(require,module,exports){
module.exports=require(14)
},{}],5690:[function(require,module,exports){
module.exports=require(57)
},{}],5691:[function(require,module,exports){
module.exports=require(15)
},{}],5692:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5689,"./lib/window.console.js":5691}],5693:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5694,"./lib/timers.js":5695}],5694:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5683,"polyfill/polyfill-base.js":5698}],5695:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5698}],5696:[function(require,module,exports){
module.exports=require(14)
},{}],5697:[function(require,module,exports){
module.exports=require(15)
},{}],5698:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5696,"./lib/window.console.js":5697}],5699:[function(require,module,exports){
module.exports=require(66)
},{}],5700:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":5699}],5701:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":5699}],5702:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":5699}],5703:[function(require,module,exports){
module.exports=require(14)
},{}],5704:[function(require,module,exports){
module.exports=require(15)
},{}],5705:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5703,"./lib/window.console.js":5704}],5706:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5707,"./lib/timers.js":5708}],5707:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5683,"polyfill/polyfill-base.js":5711}],5708:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5711}],5709:[function(require,module,exports){
module.exports=require(14)
},{}],5710:[function(require,module,exports){
module.exports=require(15)
},{}],5711:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5709,"./lib/window.console.js":5710}],5712:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":5713}],5713:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":5714,"js-ext/lib/object.js":5715}],5714:[function(require,module,exports){
module.exports=require(4)
},{}],5715:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5714,"polyfill/polyfill-base.js":5718,"utils":5719}],5716:[function(require,module,exports){
module.exports=require(14)
},{}],5717:[function(require,module,exports){
module.exports=require(15)
},{}],5718:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5716,"./lib/window.console.js":5717}],5719:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5720,"./lib/timers.js":5721}],5720:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5714,"polyfill/polyfill-base.js":5724}],5721:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5724}],5722:[function(require,module,exports){
module.exports=require(14)
},{}],5723:[function(require,module,exports){
module.exports=require(15)
},{}],5724:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5722,"./lib/window.console.js":5723}],5725:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":5683,"js-ext/lib/object.js":5686,"js-ext/lib/string.js":5688,"polyfill":5705,"polyfill/extra/transition.js":5700,"polyfill/extra/vendorCSS.js":5702}],5726:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":5683,"js-ext/lib/object.js":5686,"polyfill":5705}],5727:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":5683,"js-ext/lib/object.js":5686,"js-ext/lib/string.js":5688,"polyfill":5705}],5728:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":5681,"./attribute-extractor.js":5725,"./element-array.js":5726,"./html-parser.js":5729,"./node-parser.js":5730,"./vdom-ns.js":5731,"./vnode.js":5732,"js-ext/extra/hashmap.js":5683,"js-ext/lib/object.js":5686,"js-ext/lib/promise.js":5687,"js-ext/lib/string.js":5688,"polyfill":5705,"polyfill/extra/transition.js":5700,"polyfill/extra/transitionend.js":5701,"polyfill/extra/vendorCSS.js":5702,"utils":5706,"window-ext":5712}],5729:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":5725,"./vdom-ns.js":5731,"js-ext/extra/hashmap.js":5683,"js-ext/lib/object.js":5686,"polyfill":5705}],5730:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":5725,"./vdom-ns.js":5731,"./vnode.js":5732,"js-ext/extra/hashmap.js":5683,"js-ext/lib/object.js":5686,"polyfill":5705}],5731:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":5683,"js-ext/lib/object.js":5686,"polyfill":5705}],5732:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":5725,"./html-parser.js":5729,"./vdom-ns.js":5731,"js-ext/extra/hashmap.js":5683,"js-ext/extra/lightmap.js":5684,"js-ext/lib/array.js":5685,"js-ext/lib/object.js":5686,"js-ext/lib/string.js":5688,"polyfill":5705,"utils/lib/timers.js":5708}],5733:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":5727,"./partials/extend-element.js":5728,"./partials/node-parser.js":5730,"js-ext/extra/hashmap.js":5683,"js-ext/lib/object.js":5686,"utils/lib/timers.js":5708}],5734:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":5736,"js-ext/extra/hashmap.js":5735,"polyfill/polyfill-base.js":5741}],5735:[function(require,module,exports){
module.exports=require(4)
},{}],5736:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5735,"polyfill/polyfill-base.js":5741,"utils":5742}],5737:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":5741}],5738:[function(require,module,exports){
module.exports=require(29)
},{}],5739:[function(require,module,exports){
module.exports=require(14)
},{}],5740:[function(require,module,exports){
module.exports=require(15)
},{}],5741:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5739,"./lib/window.console.js":5740}],5742:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5743,"./lib/timers.js":5744}],5743:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5735,"polyfill/polyfill-base.js":5747}],5744:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5747}],5745:[function(require,module,exports){
module.exports=require(14)
},{}],5746:[function(require,module,exports){
module.exports=require(15)
},{}],5747:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5745,"./lib/window.console.js":5746}],5748:[function(require,module,exports){
module.exports=require(14)
},{}],5749:[function(require,module,exports){
module.exports=require(15)
},{}],5750:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5748,"./lib/window.console.js":5749}],5751:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5754}],5752:[function(require,module,exports){
module.exports=require(14)
},{}],5753:[function(require,module,exports){
module.exports=require(15)
},{}],5754:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5752,"./lib/window.console.js":5753}],5755:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],5756:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":5760,"js-ext/extra/hashmap.js":5757,"polyfill/polyfill-base.js":5766}],5757:[function(require,module,exports){
module.exports=require(4)
},{}],5758:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":5759,"../lib/object.js":5760,"./classes.js":5756,"js-ext/extra/hashmap.js":5757,"polyfill/lib/weakmap.js":5764}],5759:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":5766}],5760:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5757,"polyfill/polyfill-base.js":5766,"utils":5767}],5761:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":5766}],5762:[function(require,module,exports){
module.exports=require(29)
},{}],5763:[function(require,module,exports){
module.exports=require(14)
},{}],5764:[function(require,module,exports){
module.exports=require(57)
},{}],5765:[function(require,module,exports){
module.exports=require(15)
},{}],5766:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5763,"./lib/window.console.js":5765}],5767:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5768,"./lib/timers.js":5769}],5768:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5757,"polyfill/polyfill-base.js":5772}],5769:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5772}],5770:[function(require,module,exports){
module.exports=require(14)
},{}],5771:[function(require,module,exports){
module.exports=require(15)
},{}],5772:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5770,"./lib/window.console.js":5771}],5773:[function(require,module,exports){
module.exports=require(66)
},{}],5774:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":5773}],5775:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":5773}],5776:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":5773}],5777:[function(require,module,exports){
module.exports=require(14)
},{}],5778:[function(require,module,exports){
module.exports=require(15)
},{}],5779:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5777,"./lib/window.console.js":5778}],5780:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5781,"./lib/timers.js":5782}],5781:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5757,"polyfill/polyfill-base.js":5785}],5782:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5785}],5783:[function(require,module,exports){
module.exports=require(14)
},{}],5784:[function(require,module,exports){
module.exports=require(15)
},{}],5785:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5783,"./lib/window.console.js":5784}],5786:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":5787}],5787:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":5788,"js-ext/lib/object.js":5789}],5788:[function(require,module,exports){
module.exports=require(4)
},{}],5789:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5788,"polyfill/polyfill-base.js":5792,"utils":5793}],5790:[function(require,module,exports){
module.exports=require(14)
},{}],5791:[function(require,module,exports){
module.exports=require(15)
},{}],5792:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5790,"./lib/window.console.js":5791}],5793:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5794,"./lib/timers.js":5795}],5794:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5788,"polyfill/polyfill-base.js":5798}],5795:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5798}],5796:[function(require,module,exports){
module.exports=require(14)
},{}],5797:[function(require,module,exports){
module.exports=require(15)
},{}],5798:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5796,"./lib/window.console.js":5797}],5799:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":5757,"js-ext/lib/object.js":5760,"js-ext/lib/string.js":5762,"polyfill":5779,"polyfill/extra/transition.js":5774,"polyfill/extra/vendorCSS.js":5776}],5800:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":5757,"js-ext/lib/object.js":5760,"polyfill":5779}],5801:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":5757,"js-ext/lib/object.js":5760,"js-ext/lib/string.js":5762,"polyfill":5779}],5802:[function(require,module,exports){
module.exports=require(95)
},{"../css/element.css":5755,"./attribute-extractor.js":5799,"./element-array.js":5800,"./html-parser.js":5803,"./node-parser.js":5804,"./vdom-ns.js":5805,"./vnode.js":5806,"js-ext/extra/hashmap.js":5757,"js-ext/lib/object.js":5760,"js-ext/lib/promise.js":5761,"js-ext/lib/string.js":5762,"polyfill":5779,"polyfill/extra/transition.js":5774,"polyfill/extra/transitionend.js":5775,"polyfill/extra/vendorCSS.js":5776,"utils":5780,"window-ext":5786}],5803:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":5799,"./vdom-ns.js":5805,"js-ext/extra/hashmap.js":5757,"js-ext/lib/object.js":5760,"polyfill":5779}],5804:[function(require,module,exports){
module.exports=require(97)
},{"./attribute-extractor.js":5799,"./vdom-ns.js":5805,"./vnode.js":5806,"js-ext/extra/hashmap.js":5757,"js-ext/lib/object.js":5760,"polyfill":5779}],5805:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":5757,"js-ext/lib/object.js":5760,"polyfill":5779}],5806:[function(require,module,exports){
module.exports=require(99)
},{"./attribute-extractor.js":5799,"./html-parser.js":5803,"./vdom-ns.js":5805,"js-ext/extra/hashmap.js":5757,"js-ext/extra/lightmap.js":5758,"js-ext/lib/array.js":5759,"js-ext/lib/object.js":5760,"js-ext/lib/string.js":5762,"polyfill":5779,"utils/lib/timers.js":5782}],5807:[function(require,module,exports){
module.exports=require(100)
},{"./partials/extend-document.js":5801,"./partials/extend-element.js":5802,"./partials/node-parser.js":5804,"js-ext/extra/hashmap.js":5757,"js-ext/lib/object.js":5760,"utils/lib/timers.js":5782}],5808:[function(require,module,exports){
module.exports=require(14)
},{}],5809:[function(require,module,exports){
module.exports=require(15)
},{}],5810:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5808,"./lib/window.console.js":5809}],5811:[function(require,module,exports){
module.exports=require(4)
},{}],5812:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5811,"polyfill/polyfill-base.js":5816,"utils":5817}],5813:[function(require,module,exports){
module.exports=require(29)
},{}],5814:[function(require,module,exports){
module.exports=require(14)
},{}],5815:[function(require,module,exports){
module.exports=require(15)
},{}],5816:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5814,"./lib/window.console.js":5815}],5817:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5818,"./lib/timers.js":5819}],5818:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5811,"polyfill/polyfill-base.js":5822}],5819:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5822}],5820:[function(require,module,exports){
module.exports=require(14)
},{}],5821:[function(require,module,exports){
module.exports=require(15)
},{}],5822:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5820,"./lib/window.console.js":5821}],5823:[function(require,module,exports){
module.exports=require(14)
},{}],5824:[function(require,module,exports){
module.exports=require(15)
},{}],5825:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5823,"./lib/window.console.js":5824}],5826:[function(require,module,exports){
module.exports=require(567)
},{"js-ext/extra/hashmap.js":5811,"js-ext/lib/object.js":5812,"js-ext/lib/string.js":5813,"polyfill":5825}],5827:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5828,"./lib/timers.js":5829}],5828:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5627,"polyfill/polyfill-base.js":5832}],5829:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5832}],5830:[function(require,module,exports){
module.exports=require(14)
},{}],5831:[function(require,module,exports){
module.exports=require(15)
},{}],5832:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5830,"./lib/window.console.js":5831}],5833:[function(require,module,exports){
module.exports=require(1913)
},{"./css/scrollable.css":5154,"drag":5156,"event-mobile":5524,"js-ext/extra/hashmap.js":5627,"js-ext/lib/object.js":5628,"node-plugin":5638,"polyfill":5810,"useragent":5826,"utils":5827,"window-ext":5909}],5834:[function(require,module,exports){
module.exports=require(4)
},{}],5835:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5834,"polyfill/polyfill-base.js":5839,"utils":5840}],5836:[function(require,module,exports){
module.exports=require(29)
},{}],5837:[function(require,module,exports){
module.exports=require(14)
},{}],5838:[function(require,module,exports){
module.exports=require(15)
},{}],5839:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5837,"./lib/window.console.js":5838}],5840:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5841,"./lib/timers.js":5842}],5841:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5834,"polyfill/polyfill-base.js":5845}],5842:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5845}],5843:[function(require,module,exports){
module.exports=require(14)
},{}],5844:[function(require,module,exports){
module.exports=require(15)
},{}],5845:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5843,"./lib/window.console.js":5844}],5846:[function(require,module,exports){
module.exports=require(14)
},{}],5847:[function(require,module,exports){
module.exports=require(15)
},{}],5848:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5846,"./lib/window.console.js":5847}],5849:[function(require,module,exports){
module.exports=require(567)
},{"js-ext/extra/hashmap.js":5834,"js-ext/lib/object.js":5835,"js-ext/lib/string.js":5836,"polyfill":5848}],5850:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5851,"./lib/timers.js":5852}],5851:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":3239,"polyfill/polyfill-base.js":5855}],5852:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5855}],5853:[function(require,module,exports){
module.exports=require(14)
},{}],5854:[function(require,module,exports){
module.exports=require(15)
},{}],5855:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5853,"./lib/window.console.js":5854}],5856:[function(require,module,exports){
module.exports=require(48)
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],5857:[function(require,module,exports){
module.exports=require(11)
},{"../lib/object.js":5861,"js-ext/extra/hashmap.js":5858,"polyfill/polyfill-base.js":5867}],5858:[function(require,module,exports){
module.exports=require(4)
},{}],5859:[function(require,module,exports){
module.exports=require(51)
},{"../lib/array.js":5860,"../lib/object.js":5861,"./classes.js":5857,"js-ext/extra/hashmap.js":5858,"polyfill/lib/weakmap.js":5865}],5860:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":5867}],5861:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5858,"polyfill/polyfill-base.js":5867,"utils":5868}],5862:[function(require,module,exports){
module.exports=require(54)
},{"polyfill":5867}],5863:[function(require,module,exports){
module.exports=require(29)
},{}],5864:[function(require,module,exports){
module.exports=require(14)
},{}],5865:[function(require,module,exports){
module.exports=require(57)
},{}],5866:[function(require,module,exports){
module.exports=require(15)
},{}],5867:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5864,"./lib/window.console.js":5866}],5868:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5869,"./lib/timers.js":5870}],5869:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5858,"polyfill/polyfill-base.js":5873}],5870:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5873}],5871:[function(require,module,exports){
module.exports=require(14)
},{}],5872:[function(require,module,exports){
module.exports=require(15)
},{}],5873:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5871,"./lib/window.console.js":5872}],5874:[function(require,module,exports){
module.exports=require(66)
},{}],5875:[function(require,module,exports){
module.exports=require(67)
},{"../bin/local-hashmap.js":5874}],5876:[function(require,module,exports){
module.exports=require(68)
},{"../bin/local-hashmap.js":5874}],5877:[function(require,module,exports){
module.exports=require(69)
},{"../bin/local-hashmap.js":5874}],5878:[function(require,module,exports){
module.exports=require(14)
},{}],5879:[function(require,module,exports){
module.exports=require(15)
},{}],5880:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5878,"./lib/window.console.js":5879}],5881:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5882,"./lib/timers.js":5883}],5882:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5858,"polyfill/polyfill-base.js":5886}],5883:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5886}],5884:[function(require,module,exports){
module.exports=require(14)
},{}],5885:[function(require,module,exports){
module.exports=require(15)
},{}],5886:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5884,"./lib/window.console.js":5885}],5887:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":5888}],5888:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":5889,"js-ext/lib/object.js":5890}],5889:[function(require,module,exports){
module.exports=require(4)
},{}],5890:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5889,"polyfill/polyfill-base.js":5893,"utils":5894}],5891:[function(require,module,exports){
module.exports=require(14)
},{}],5892:[function(require,module,exports){
module.exports=require(15)
},{}],5893:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5891,"./lib/window.console.js":5892}],5894:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5895,"./lib/timers.js":5896}],5895:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5889,"polyfill/polyfill-base.js":5899}],5896:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5899}],5897:[function(require,module,exports){
module.exports=require(14)
},{}],5898:[function(require,module,exports){
module.exports=require(15)
},{}],5899:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5897,"./lib/window.console.js":5898}],5900:[function(require,module,exports){
module.exports=require(92)
},{"js-ext/extra/hashmap.js":5858,"js-ext/lib/object.js":5861,"js-ext/lib/string.js":5863,"polyfill":5880,"polyfill/extra/transition.js":5875,"polyfill/extra/vendorCSS.js":5877}],5901:[function(require,module,exports){
module.exports=require(93)
},{"js-ext/extra/hashmap.js":5858,"js-ext/lib/object.js":5861,"polyfill":5880}],5902:[function(require,module,exports){
module.exports=require(94)
},{"js-ext/extra/hashmap.js":5858,"js-ext/lib/object.js":5861,"js-ext/lib/string.js":5863,"polyfill":5880}],5903:[function(require,module,exports){
arguments[4][95][0].apply(exports,arguments)
},{"../css/element.css":5856,"./attribute-extractor.js":5900,"./element-array.js":5901,"./html-parser.js":5904,"./node-parser.js":5905,"./vdom-ns.js":5906,"./vnode.js":5907,"js-ext/extra/hashmap.js":5858,"js-ext/lib/object.js":5861,"js-ext/lib/promise.js":5862,"js-ext/lib/string.js":5863,"polyfill":5880,"polyfill/extra/transition.js":5875,"polyfill/extra/transitionend.js":5876,"polyfill/extra/vendorCSS.js":5877,"utils":5881,"window-ext":5887}],5904:[function(require,module,exports){
module.exports=require(96)
},{"./attribute-extractor.js":5900,"./vdom-ns.js":5906,"js-ext/extra/hashmap.js":5858,"js-ext/lib/object.js":5861,"polyfill":5880}],5905:[function(require,module,exports){
arguments[4][97][0].apply(exports,arguments)
},{"./attribute-extractor.js":5900,"./vdom-ns.js":5906,"./vnode.js":5907,"js-ext/extra/hashmap.js":5858,"js-ext/lib/object.js":5861,"polyfill":5880}],5906:[function(require,module,exports){
module.exports=require(98)
},{"js-ext/extra/hashmap.js":5858,"js-ext/lib/object.js":5861,"polyfill":5880}],5907:[function(require,module,exports){
"use strict";
/**
* Delivers the `vnode` prototype object, which is a virtualisation of an `Element` inside the Dom.
* These Elements work smoothless with the vdom (see ...).
*
* vnodes are much quicker to access and walk through than native dom-nodes. However, this is a module you don't need
* by itself: `Element`-types use these features under the hood.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* <br>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
*
* @module vdom
* @submodule vnode
* @class vnode
* @since 0.0.1
*/
require('js-ext/lib/array.js');
require('js-ext/lib/object.js');
require('js-ext/lib/string.js');
require('polyfill');
var createHashMap = require('js-ext/extra/hashmap.js').createMap;
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.VNode) {
return window._ITSAmodules.VNode; // VNODE was already created
}
console.warn('VDOM 1');
var NS = require('./vdom-ns.js')(window),
extractor = require('./attribute-extractor.js')(window),
DOCUMENT = window.document,
LightMap = require('js-ext/extra/lightmap.js'),
MUTATION_EVENTS = new LightMap(),
BATCH_WILL_RUN = false,
xmlNS = NS.xmlNS,
nodeids = NS.nodeids,
htmlToVNodes = require('./html-parser.js')(window),
timers = require('utils/lib/timers.js'),
async = timers.async,
later = timers.later,
/*jshint proto:true */
PROTO_SUPPORTED = !!Object.__proto__,
/*jshint proto:false */
// cleanup memory after 1 minute: removed nodes SHOULD NOT be accessed afterwards
// because vnode would be recalculated and might be different from before
DESTROY_DELAY = 60000,
unescapeEntities = NS.UnescapeEntities,
NTH_CHILD_REGEXP = /^(?:(\d*)[n|N])([\+|\-](\d+))?$/, // an+b
STRING = 'string',
CLASS = 'class',
STYLE = 'style',
ID = 'id',
NODE= 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
EV_REMOVED = NODE+REMOVE,
EV_INSERTED = NODE+INSERT,
EV_CONTENT_CHANGE = NODE+'content'+CHANGE,
EV_ATTRIBUTE_REMOVED = ATTRIBUTE+REMOVE,
EV_ATTRIBUTE_CHANGED = ATTRIBUTE+CHANGE,
EV_ATTRIBUTE_INSERTED = ATTRIBUTE+INSERT,
SPLIT_CHARACTER = createHashMap({
' ': true,
'>': true,
'+': true, // only select the element when it is immediately preceded by the former element
'~': true // only the element when it has the former element as a sibling. (just like `+`, but less strict)
}),
STORABLE_SPLIT_CHARACTER = createHashMap({
'>': true,
'+': true,
'~': true
}),
SIBLING_MATCH_CHARACTER = createHashMap({
'+': true,
'~': true
}),
ATTR_DETAIL_SPECIFIERS = createHashMap({
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to attribute-name end-tokens.
*
* @property END_ATTRIBUTENAME
* @default {
* '=': true,
* ']': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
END_ATTRIBUTENAME = createHashMap({
'=': true,
']': true,
'^': true, // “begins with” selector
'$': true, // “ends with” selector
'*': true, // “contains” selector (might be a substring)
'~': true, // “contains” selector as a separate word, separated by spaces
'|': true // “contains” selector as a separate word, separated by `|`
}),
/**
* Object to gain quick access to different changes of Element nodeType changes.
*
* @property NODESWITCH
* @default {
* 1: {
* 1: 1,
* 3: 2,
* 8: 3
* },
* 3: {
* 1: 4,
* 3: 5,
* 8: 6
* },
* 8: {
* 1: 7,
* 3: 8,
* 8: 9
* }
* }
* @type Object
* @protected
* @since 0.0.1
*/
NODESWITCH = createHashMap({
1: createHashMap({
1: 1, // oldNodeType==Element, newNodeType==Element
3: 2, // oldNodeType==Element, newNodeType==TextNode
8: 3 // oldNodeType==Element, newNodeType==Comment
}),
3: createHashMap({
1: 4, // oldNodeType==TextNode, newNodeType==Element
3: 5, // oldNodeType==TextNode, newNodeType==TextNode
8: 6 // oldNodeType==TextNode, newNodeType==Comment
}),
8: createHashMap({
1: 7, // oldNodeType==Comment, newNodeType==Element
3: 8, // oldNodeType==Comment, newNodeType==TextNode
8: 9 // oldNodeType==Comment, newNodeType==Comment
})
}),
/**
* Object to gain quick access to selector start-tokens.
*
* @property SELECTOR_IDENTIFIERS
* @default {
* '#': 1,
* '.': 2,
* '[': 3
* }
* @type Object
* @protected
* @since 0.0.1
*/
SELECTOR_IDENTIFIERS = createHashMap({
'#': 1,
'.': 2,
'[': 3,
':': 4
}),
PSEUDO_FIRST_CHILD = ':first-child',
PSEUDO_FIRST_OF_TYPE = ':first-of-type',
PSEUDO_LAST_CHILD = ':last-child',
PSEUDO_LAST_OF_TYPE = ':last-of-type',
PSEUDO_NTH_CHILD = ':nth-child',
PSEUDO_NTH_LAST_CHILD = ':nth-last-child',
PSEUDO_NTH_LAST_OF_TYPE = ':nth-last-of-type',
PSEUDO_NTH_OF_TYPE = ':nth-of-type',
PSEUDO_ONLY_OF_TYPE = ':only-of-type',
PSEUDO_ONLY_CHILD = ':only-child',
/**
* Object to gain quick access to the selectors that required children
*
* @property PSEUDO_REQUIRED_CHILDREN
* @default {
* ':first-child': true,
* ':first-of-type': true,
* ':last-child': true,
* ':last-of-type': true,
* ':nth-child': true,
* ':nth-last-child': true,
* ':nth-last-of-type': true,
* ':nth-of-type': true,
* ':only-of-type': true,
* ':only-child': true
* }
* @type Object
* @protected
* @since 0.0.1
*/
PSEUDO_REQUIRED_CHILDREN = createHashMap(),
_matchesSelectorItem, _matchesOneSelector, _findElementSibling, vNodeProto, _markRemoved, _tryReplaceChild,
_splitSelector, _findNodeSibling, _matchNthChild, _batchEmit, _emitDestroyChildren, _tryRemoveDomNode;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_FIRST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_CHILD] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_LAST_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_NTH_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_OF_TYPE] = true;
PSEUDO_REQUIRED_CHILDREN[PSEUDO_ONLY_CHILD] = true;
/**
* Searches for the next -or previous- node-sibling (nodeType of 1, 3 or 8).
*
* @method _findNodeSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findNodeSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
index = vParent.vChildNodes.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildNodes[index];
};
/**
* Searches for the next -or previous- Element-sibling (nodeType of 1).
*
* @method _findElementSibling
* @param vnode {Object} the vnode to inspect
* @param [next] {Boolean} whether to search for the next, or previous match.
* @return {Object|undefined} the vnode that matches the search
* @protected
* @private
* @since 0.0.1
*/
_findElementSibling = function(vnode, next) {
var vParent = vnode.vParent,
index;
if (!vParent || !vParent.vChildNodes) {
return;
}
if (vnode.nodeType===1) {
index = vParent.vChildren.indexOf(vnode) + (next ? 1 : -1);
return vParent.vChildren[index];
}
else {
/*jshint noempty:true */
while ((vnode=_findNodeSibling(vnode, next)) && (vnode.nodeType!==1)) {}
/*jshint noempty:false */
return vnode;
}
};
/**
* Check whether the vnode matches a "nth-child" test, which is used for css pseudoselectors like `nth-child`, `nth-of-type` etc.
*
* @method _matchNthChild
* @param pseudoArg {String} the argument for nth-child
* @param index {Number} the index of the inspected vnode
* @return {Boolean} whether the vnode matches the nthChild test
* @protected
* @private
* @since 0.0.1
*/
_matchNthChild = function(pseudoArg, index) {
var match, k, a, b, nodeOk, nthIndex, sign, isNumber;
(pseudoArg==='even') && (pseudoArg='2n');
(pseudoArg==='odd') && (pseudoArg='2n+1');
match = pseudoArg.match(NTH_CHILD_REGEXP) || (isNumber=pseudoArg.validateNumber());
if (!match) {
return false;
}
// pseudoArg follows the pattern: `an+b`
if (!isNumber) {
a = match[1];
sign = match[2];
b = match[3] || 0;
(b==='') && (b=0);
}
else {
b = pseudoArg;
}
sign && (sign=sign[0]);
if (!a) {
// only fixed index to match
return (sign==='-') ? false : (parseInt(b, 10)===index);
}
else {
// we need to iterate
nodeOk = false;
b = window.Number(b);
for (k=0; !nodeOk; k++) {
nthIndex = (sign==='-') ? (a*k) - b : (a*k) + b;
if (nthIndex===index) {
nodeOk = true;
}
else if (nthIndex>index) {
// beyond index --> will never become a fix anymore
return false;
}
}
return nodeOk;
}
};
/**
* Check whether the vnode matches the css-selector. the css-selector should be a single selector,
* not multiple, so it shouldn't contain a `comma`.
*
* @method _matchesOneSelector
* @param vnode {vnode} the vnode to inspect
* @param selector {String} the selector-item to check the match for
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches the css-selector
* @protected
* @private
* @since 0.0.1
*/
_matchesOneSelector = function(vnode, selector, relatedVNode) {
var selList = _splitSelector(selector),
size = selList.length,
selMatch = false,
i, selectorItem, last,
rightvnode, relationMatch, checkRelation;
if (STORABLE_SPLIT_CHARACTER[selList[size-1]]) {
return false;
}
relationMatch = function(leftVNode, rightVNode, relationCharacter) {
var match, vParent, vChildren;
// when `selector` starts with `>`, `~` or `+`, then
// there should also be a match comparing a related node!
switch (relationCharacter) {
case '>':
leftVNode || (leftVNode=rightVNode.vParent);
match = (leftVNode.vChildren.indexOf(rightVNode)!==-1);
break;
case '~':
leftVNode || (leftVNode=rightVNode.vFirstElement);
vParent = leftVNode.vParent;
vChildren = vParent && vParent.vChildren;
match = vParent && (vChildren.indexOf(leftVNode)<vChildren.indexOf(rightVNode));
break;
case '+':
leftVNode || (leftVNode=rightVNode.vPreviousElement);
match = (leftVNode.vNextElement === rightVNode);
}
return !!match;
};
last = size - 1;
i = last;
while (vnode && ((selMatch || (i===last)) && (i>=0))) {
selectorItem = selList[i];
if (STORABLE_SPLIT_CHARACTER[selectorItem]) {
checkRelation = selectorItem;
i--;
}
else {
selMatch = _matchesSelectorItem(vnode, selectorItem);
if (!selMatch && (i===last)) {
return false;
}
while (!selMatch && vnode && (i!==last)) {
// may also look at nodes higher in the chain
vnode = SIBLING_MATCH_CHARACTER[selList[i+1]] ? vnode.vPreviousElement : vnode.vParent;
selMatch = vnode && _matchesSelectorItem(vnode, selectorItem);
}
selMatch && checkRelation && vnode && (selMatch=relationMatch(vnode, rightvnode, checkRelation));
rightvnode = vnode;
if (vnode) {
// do a precheck on the next checkRelation:
// and determine whether to set the vnode upper the tree or at the previous sibling:
vnode = SIBLING_MATCH_CHARACTER[selList[i-1]] ? vnode.vPreviousElement : vnode.vParent;
}
if (selMatch) {
checkRelation = false;
i--;
}
}
}
if ((rightvnode===relatedVNode) || (i!==-1)) {
selMatch = false;
}
selMatch && checkRelation && rightvnode && (selMatch=relationMatch(relatedVNode, rightvnode, checkRelation));
return selMatch;
};
/**
* Check whether the vnode matches one specific selector-item. Suppose the css-selector: "#mynode li.red .blue"
* then there are 3 selector-items: "#mynode", "li.red" and ".blue"
*
* This method also can handle the new selectors:
* <ul>
* <li>[att^=val] –-> the “begins with” selector</li>
* <li>[att$=val] –-> the “ends with” selector</li>
* <li>[att*=val] –-> the “contains” selector (might be a substring)</li>
* <li>[att~=val] –-> the “contains” selector as a separate word, separated by spaces</li>
* <li>[att|=val] –-> the “contains” selector as a separate word, separated by `|`</li>
* <li>+ --> (same level)</li>
* <li>~ --> (same level)</li>
* </ul>
*
* @method _matchesSelectorItem
* @param vnode {Object} the vnode to inspect
* @param selectorItem {String} the selector-item to check the match for
* @return {Boolean} whether the vnode matches the selector-item
* @protected
* @private
* @since 0.0.1
*/
_matchesSelectorItem = function (vnode, selectorItem) {
var i = 0,
len = selectorItem.length,
character = selectorItem[0],
tagName, id, className, attributeName, attributeValue, stringMarker, attributeisString, isBoolean, insideAttributeValue, insideAttribute,
vParent, checkBoolean, treatment, k, min, max, value, len2, index, found, pseudo, pseudoArg, arglevel, count, vParentVChildren;
if (selectorItem==='*') {
return true;
}
if (!SELECTOR_IDENTIFIERS[character]) {
// starts with tagName
tagName = '';
// reposition i to continue in the right way:
i--;
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
tagName += character;
}
if (tagName.toUpperCase()!==vnode.tag) {
return false;
}
}
while (i<len) {
switch (character) {
case '#':
id = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
id += character;
}
if (id!==vnode.id) {
return false;
}
break;
case '.':
className = '';
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
className += character;
}
if (!vnode.hasClass(className)) {
return false;
}
break;
case '[':
attributeName = '';
while ((++i<len) && (character=selectorItem[i]) && !END_ATTRIBUTENAME[character]) {
attributeName += character;
}
// if character===']' then we have an attribute without a value-definition
if (!vnode.attrs[attributeName] || ((character===']') && (vnode.attrs[attributeName]!==''))) {
return !!vnode.attrs[attributeName];
}
// now we read the value of the attribute
// however, it could be that the selector has a special `detailed` identifier set (defined by: ATTR_DETAIL_SPECIFIERS)
if (ATTR_DETAIL_SPECIFIERS[character]) {
treatment = character; // store the character to know how the attributedata should be treaded
i++; // character should be a "=" by now
}
else {
treatment = null;
}
attributeValue = '';
stringMarker = selectorItem[i+1];
attributeisString = (stringMarker==='"') || (stringMarker==="'");
attributeisString && (i++);
// end of attributaValue = (character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker))
while ((++i<len) && (character=selectorItem[i]) && !((character===']') && (!attributeisString || (selectorItem[i-1]===stringMarker)))) {
attributeValue += character;
}
if (attributeisString) {
// if attribute is string, then we need to _remove to last stringmarker
attributeValue = attributeValue.substr(0, attributeValue.length-1);
}
else {
// if attribute is no string, then we need to typecast its value
isBoolean = ((attributeValue.length>3) && (attributeValue.length<6) &&
(checkBoolean=attributeValue.toUpperCase()) &&
((checkBoolean==='FALSE') || (checkBoolean==='TRUE')));
// typecast the value to either Boolean or Number:
attributeValue = isBoolean ? (checkBoolean==='TRUE') : parseFloat(attributeValue);
}
// depending upon how the attributedata should be treated:
if (treatment) {
switch (treatment) {
case '^': // “begins with” selector
if (!vnode.attrs[attributeName].startsWith(attributeValue)) {
return false;
}
break;
case '$': // “ends with” selector
if (!vnode.attrs[attributeName].endsWith(attributeValue)) {
return false;
}
break;
case '*': // “contains” selector (might be a substring)
if (!vnode.attrs[attributeName].contains(attributeValue)) {
return false;
}
break;
case '~': // “contains” selector as a separate word, separated by spaces
if (!(' '+vnode.attrs[attributeName]+' ').contains(' '+attributeValue+' ')) {
return false;
}
break;
case '|': // “contains” selector as a separate word, separated by `|`
if (!('|'+vnode.attrs[attributeName]+'|').contains('|'+attributeValue+'|')) {
return false;
}
break;
}
}
else if (vnode.attrs[attributeName]!==attributeValue) {
return false;
}
// we still need to increase one position:
(++i<len) && (character=selectorItem[i]);
break;
case ':':
// we have a pseudo-selector
// first, find out which one
// because '::' is a valid start (though without any selection), we start to back the next character as well:
pseudo = ':'+selectorItem[++i];
pseudoArg = '';
vParent = vnode.vParent;
vParentVChildren = vParent && vParent.vChildren;
// pseudo-selectors might have an argument passed in, like `:nth-child(2n+1)` or `:not([type="checkbox"])` --> we
// store this argument inside `pseudoArg`
// also note that combinations are possible with `:not` --> `:not(:nth-child(2n+1))`
// also note that we cannot "just" look for a closing character when running into the usage of attributes:
// for example --> `:not([data-x="some data :)"])`
// that's why -once we are inside attribute-data- we need to continue until the attribute-data ends
while ((++i<len) && (character=selectorItem[i]) && !SELECTOR_IDENTIFIERS[character]) {
if (character==='(') {
// starting arguments
arglevel = 1;
insideAttribute = false;
insideAttributeValue = false;
while ((++i<len) && (character=selectorItem[i]) && (arglevel>0)) {
if (!insideAttribute) {
if (character==='(') {
arglevel++;
}
else if (character===')') {
arglevel--;
}
else if (character==='[') {
insideAttribute = true;
}
}
else {
// inside attribute
if (!insideAttributeValue) {
if ((character==='"') || (character==="'")) {
insideAttributeValue = true;
stringMarker = character;
}
else if (character===']') {
insideAttribute = false;
}
}
else if ((character===stringMarker) && (selectorItem[i+1]===']')) {
insideAttributeValue = false;
}
}
(arglevel>0) && (pseudoArg+=character);
}
}
else {
pseudo += character;
}
}
// now, `pseudo` is known as well as its possible pseudoArg
if (!vParentVChildren && PSEUDO_REQUIRED_CHILDREN[pseudo]) {
return false;
}
switch (pseudo) {
case ':checked': // input:checked Selects every checked <input> element
if (!vnode.attrs.checked) {
return false;
}
break;
case ':disabled': // input:disabled Selects every disabled <input> element
if (!vnode.attrs.disabled) {
return false;
}
break;
case ':empty': // p:empty Selects every <p> element that has no children (including text nodes)
if (vnode.vChildNodes && (vnode.vChildNodes.length>0)) {
return false;
}
break;
case ':enabled': // input:enabled Selects every enabled <input> element
if (vnode.attrs.disabled) {
return false;
}
break;
case PSEUDO_FIRST_CHILD: // p:first-child Selects every <p> element that is the first child of its parent
if (vParentVChildren[0]!==vnode) {
return false;
}
break;
case PSEUDO_FIRST_OF_TYPE: // p:first-of-type Selects every <p> element that is the first <p> element of its parent
for (k=vParentVChildren.indexOf(vnode)-1; k>=0; k--) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':focus': // input:focus Selects the input element which has focus
if (vnode.domNode!==DOCUMENT.activeElement) {
return false;
}
break;
case ':in-range': // input:in-range Selects input elements with a value within a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || (value<min) || (value>max)) {
return false;
}
break;
case ':lang': // p:lang(it) Selects every <p> element with a lang attribute equal to "it" (Italian)
if (vnode.attrs.lang!==pseudoArg) {
return false;
}
break;
case PSEUDO_LAST_CHILD: // p:last-child Selects every <p> element that is the last child of its parent
if (vParentVChildren[vParentVChildren.length-1]!==vnode) {
return false;
}
break;
case PSEUDO_LAST_OF_TYPE: // p:last-of-type Selects every <p> element that is the last <p> element of its parent
len2 = vParentVChildren.length;
for (k=vParentVChildren.indexOf(vnode)+1; k<len2; k++) {
if (vParentVChildren[k].tag===vnode.tag) {
return false;
}
}
break;
case ':not': // :not(p) Selects every element that is not a <p> element
if (vnode.matchesSelector(pseudoArg)) {
return false;
}
break;
case PSEUDO_NTH_CHILD: // p:nth-child(2) Selects every <p> element that is the second child of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
index = vParentVChildren.indexOf(vnode)+1;
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_CHILD: // p:nth-last-child(2) Selects every <p> element that is the second child of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
if (!_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_LAST_OF_TYPE: // p:nth-last-of-type(2) Selects every <p> element that is the second <p> element of its parent, counting from the last child
// NOTE: css `nth` starts with 1 instead of 0 !!!
// Also, nth-last-child counts from bottom up
index = vParentVChildren.length - vParentVChildren.indexOf(vnode);
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
index = 0;
for (k=vParentVChildren.length-1; (k>=0) && !found; k--) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_NTH_OF_TYPE: // p:nth-of-type(2) Selects every <p> element that is the second <p> element of its parent
// NOTE: css `nth` starts with 1 instead of 0 !!!
found = false;
len2 = vParentVChildren.length;
index = 0;
for (k=0; (k<len2) && !found; k++) {
(vParentVChildren[k].tag===vnode.tag) && index++;
(vParentVChildren[k]===vnode) && (found=true);
}
if (!found || !_matchNthChild(pseudoArg, index)) {
return false;
}
break;
case PSEUDO_ONLY_OF_TYPE: // p:only-of-type Selects every <p> element that is the only <p> element of its parent
len2 = vParentVChildren.length;
count = 0;
for (k=0; (k<len2) && (count<=1); k++) {
(vParentVChildren[k].tag===vnode.tag) && count++;
}
if (count!==1) {
return false;
}
break;
case PSEUDO_ONLY_CHILD: // p:only-child Selects every <p> element that is the only child of its parent
if (vParentVChildren.length!==1) {
return false;
}
break;
case ':optional': // input:optional Selects input elements with no "required" attribute
if (vnode.attrs.required) {
return false;
}
break;
case ':out-of-range': // input:out-of-range Selects input elements with a value outside a specified range
if ((vnode.tag!=='INPUT') || ((vnode.attrs.type || '').toLowerCase()!=='number')) {
return false;
}
min = parseInt(vnode.attrs.min, 10);
max = parseInt(vnode.attrs.max, 10);
value = parseInt(vnode.domNode.value, 10);
if (!value || !min || !max || ((value>=min) && (value<=max))) {
return false;
}
break;
case ':read-only': // input:read-only Selects input elements with the "readonly" attribute specified
if (!vnode.attrs.readonly) {
return false;
}
break;
case ':read-write': // input:read-write Selects input elements with the "readonly" attribute NOT specified
if (vnode.attrs.readonly) {
return false;
}
break;
case ':required': // input:required Selects input elements with the "required" attribute specified
if (!vnode.attrs.required) {
return false;
}
break;
case ':root': // Selects the document's root element
if (vnode.domNode!==DOCUMENT.documentElement) {
return false;
}
break;
}
}
}
return true;
};
/**
* Splits the selector into separate subselector-items that should match different elements through the tree.
* Special characters '>' and '+' are added as separate items in the hash.
*
* @method _splitSelector
* @param selector {String} the selector-item to check the match for
* @return {Array} splitted selectors
* @protected
* @private
* @since 0.0.1
*/
_splitSelector = function(selector) {
var list = [],
len = selector.length,
sel = '',
i, character, insideDataAttr;
for (i=0; i<len; i++) {
character = selector[i];
if (character==='[') {
sel += character;
insideDataAttr = true;
}
else if (character===']') {
sel += character;
insideDataAttr = false;
}
else if (insideDataAttr || !SPLIT_CHARACTER[character]) {
sel += character;
}
else {
// unique selectoritem is found, add it to the list
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
// in case the last character was '>', '+' or '~', we need to add it as a separate item
STORABLE_SPLIT_CHARACTER[character] && (list[list.length]=character);
}
}
// add the last item
if (sel.length>0) {
list[list.length] = sel;
sel = '';
}
return list;
};
_batchEmit = function() {
// we will exactly define the 'UI:'-event --> Itags will have another emitterName
MUTATION_EVENTS.each(function (mutationEvents, vnode) {
var domNode = vnode.domNode;
if (mutationEvents[EV_REMOVED]) {
domNode.emit('UI:'+EV_REMOVED);
}
else if (mutationEvents[EV_INSERTED]) {
domNode.emit('UI:'+EV_INSERTED);
}
else {
// contentchange and attributechanges can go hand in hand
mutationEvents.each(function(value, evt) {
domNode.emit('UI:'+evt, (evt===EV_CONTENT_CHANGE) ? null : {changed: value});
});
}
});
MUTATION_EVENTS.clear();
BATCH_WILL_RUN = false;
};
_emitDestroyChildren = function(vnode) {
var children = vnode.vChildren,
len = children.length,
i, vChild;
for (i=0; i<len; i++) {
vChild = children[i];
vChild._emit(EV_REMOVED);
}
};
_markRemoved = function(vnode) {
var vChildNodes = vnode.vChildNodes,
len, i, vChildNode;
if (vnode.nodeType===1) {
Object.protectedProp(vnode, 'removedFromDOM', true);
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && _markRemoved(vChildNode);
}
}
}
};
/**
* A safe way to remove a dom-node, even if it is not present.
* Will not throw a JS error when the "to be removed" node isn't in the dom
*
* @method _tryRemoveDomNode
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param childDomNode {DOMNode} the node that needs to be removed
* @since 0.0.1
*/
_tryRemoveDomNode = function(parentDomNode, childDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && childDomNode.isItag && childDomNode.isItag()) {
DOCUMENT._itagList.remove(childDomNode);
}
try {
parentDomNode._removeChild(childDomNode);
}
catch(err) {}
};
/**
* A safe way to replace a dom-node, even if the "to be replaced"-node it is not present.
* Will not throw a JS error when the "to be replaced" node isn't in the dom. in that case, the new node will
* be appended
*
* @method _tryReplaceChild
* @param parentDomNode {DOMNode} the parentNode that holds the node that needs to be removed
* @param newChildDomNode {DOMNode} the node that needs to be replaced
* @param oldChildDomNode {DOMNode} the node that needs to be inserted
* @since 0.0.1
*/
_tryReplaceChild = function(parentDomNode, newChildDomNode, oldChildDomNode) {
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && oldChildDomNode.isItag && oldChildDomNode.isItag()) {
DOCUMENT._itagList.remove(oldChildDomNode);
}
try {
parentDomNode._replaceChild(newChildDomNode, oldChildDomNode);
}
catch(err) {
// if the childDomNode isn;t there any more - for whatever reason - the we need to append the newChildNode
parentDomNode._appendChild(newChildDomNode);
}
};
vNodeProto = window._ITSAmodules.VNode = {
/**
* Check whether the vnode's domNode is equal, or contains the specified Element.
*
* @method contains
* @return {Boolean} whether the vnode's domNode is equal, or contains the specified Element.
* @since 0.0.1
*/
contains: function(otherVNode, noProtectedSearch) {
var instance = this;
if (otherVNode && (otherVNode.destroyed || (noProtectedSearch && otherVNode._systemNode))) {
return false;
}
while (otherVNode && (otherVNode!==instance)) {
otherVNode = otherVNode.vParent;
if (otherVNode && noProtectedSearch && (otherVNode._systemNode || (otherVNode.isItag && otherVNode.domNode.contentHidden))) {
return false;
}
}
return (otherVNode===instance);
},
/**
* Empties the vnode.
*
* Syncs with the dom.
*
* @method empty
* @param [full=false] {Boolean} whether system-nodes should be removed as well
* @chainable
* @since 0.0.1
*/
empty: function(full) {
delete this._scripts;
return this._setChildNodes([], null, full);
},
/**
* Returns the first child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method firstOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the first child-vnode or null when not present
* @since 0.0.1
*/
firstOfVChildren: function(cssSelector) {
var instance = this,
found, i, len, vChildren, element;
if (!cssSelector) {
return instance.vFirstElementChild;
}
vChildren = instance.vChildren;
len = vChildren.length;
for (i=0; !found && (i<len); i++) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
return found;
},
/**
* Gets the innerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* Only valid for nodetype=1 (HTMLElements)
*
* @method getHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String|undefined} the innerHTML without the elements specified, or `undefined` when not an HTMLElement
* @since 0.0.1
*/
getHTML: function(exclude, includeSystemNodes) {
var instance = this,
html, vChildNodes, len, i, vChildNode;
if (instance.nodeType===1) {
if (exclude) {
Array.isArray(exclude) || (exclude=[exclude]);
}
html = '';
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
switch (vChildNode.nodeType) {
case 1:
(exclude && exclude.contains(vChildNode.domNode)) || (!includeSystemNodes && vChildNode._systemNode) || (html+=vChildNode.getOuterHTML(exclude, includeSystemNodes));
break;
case 3:
html += vChildNode.text.replace(/</g, '<').replace(/>/g, '>');
break;
case 8:
html += '<!--' + vChildNode.text.replace(/</g, '<').replace(/>/g, '>') + '-->';
}
}
}
return html;
},
/**
* Gets the outerHTML of the vnode representing the dom-node.
* You may exclude HTMLElement (node-type=1) by specifying `exclude`.
*
* @method getOuterHTML
* @param [exclude] {Array|HTMLElement} an array of HTMLElements - or just 1 - to be excluded
* @param [includeSystemNodes=false] {Boolean} whether system-nodes and i-tag inner-content should be returned. By default, they stay hidden.
* @return {String} the outerHTML
* @since 0.0.1
*/
getOuterHTML: function(exclude, includeSystemNodes) {
var instance = this,
html,
attrs = instance.attrs;
if (instance.nodeType===1) {
if (instance.nodeType!==1) {
return instance.textContent;
}
html = '<' + instance.tag.toLowerCase();
attrs.each(function(value, key) {
html += ' '+key+'="'+value+'"';
});
instance.isVoid && (html += '/');
html += '>';
if (!instance.isVoid) {
html += ((!includeSystemNodes && instance.isItag) ? '' : instance.getHTML(exclude, includeSystemNodes)) + '</' + instance.tag.toLowerCase() + '>';
}
}
return html;
},
/**
* Checks whether the vnode has any vChildNodes (nodeType of 1, 3 or 8).
*
* @method hasVChildNodes
* @return {Boolean} whether the vnode has any vChildNodes.
* @since 0.0.1
*/
hasVChildNodes: function() {
return this.vChildNodes ? (this.vChildNodes.length>0) : false;
},
/**
* Checks whether the vnode has any vChildren (vChildNodes with nodeType of 1).
*
* @method hasVChildren
* @return {Boolean} whether the vnode has any vChildren.
* @since 0.0.1
*/
hasVChildren: function() {
return this.vChildNodes ? (this.vChildren.length>0) : false;
},
/**
* Checks whether the className is present on the vnode.
*
* @method hasClass
* @param className {String|Array} the className to check for. May be an Array of classNames, which all needs to be present.
* @return {Boolean} whether the className (or classNames) is present on the vnode
* @since 0.0.1
*/
hasClass: function(className) {
var instance = this,
check = function(cl) {
return !!instance.classNames[cl];
};
if (!instance.classNames) {
return false;
}
if (typeof className === STRING) {
return check(className);
}
else if (Array.isArray(className)) {
return className.every(check);
}
return false;
},
/**
* Returns the last child-vnode (if any). The child represents an Element (nodeType===1).
*
* @method lastOfVChildren
* @param cssSelector {String} one or more css-selectors
* @return {Object|null} the last child-vnode or null when not present
* @since 0.0.1
*/
lastOfVChildren: function(cssSelector) {
var vChildren = this.vChildren,
found, i, element;
if (vChildren) {
if (!cssSelector) {
return this.vLastElementChild;
}
for (i=vChildren.length-1; !found && (i>=0); i--) {
element = vChildren[i];
element.matchesSelector(cssSelector) && (found=element);
}
}
return found;
},
/**
* Checks whether the vnode matches one of the specified selectors. `selectors` can be one, or multiple css-selectors,
* separated by a `comma`. For example: "#myid li.red blue" is one selector, "div.red, div.blue, div.green" are three selectors.
*
* @method matchesSelector
* @param selectors {String} one or more css-selectors
* @param [relatedVNode] {vnode} a related vnode where to selectors starting with `>`, `~` or `+` should be compared.
* If not specified, any of these three starting selector-characters will be ignored (leading to matching this first character).
* @return {Boolean} whether the vnode matches one of the selectors
* @since 0.0.1
*/
matchesSelector: function(selectors, relatedVNode) {
var instance = this;
if (instance.nodeType!==1) {
return false;
}
selectors = selectors.split(',');
// we can use Array.some, because there won't be many separated selectoritems,
// so the final invocation won't be delayed much compared to looping
return selectors.some(function(selector) {
return _matchesOneSelector(instance, selector, relatedVNode);
});
},
/**
* Reloads the DOM-attribute into the vnode.
*
* @method matchesSelector
* @param attributeName {String} the name of the attribute to be reloaded.
* @return {Node} the domNode that was reloaded.
* @since 0.0.1
*/
reloadAttr: function(attributeName) {
var instance = this,
domNode = instance.domNode,
attributeValue = domNode._getAttribute(attributeName),
attrs = instance.attrs,
extractStyle, extractClass;
if (instance.nodeType===1) {
attributeValue || (attributeValue='');
if (attributeValue==='') {
delete attrs[attributeName];
// in case of STYLE attributeName --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attributeName --> special treatment
(attributeName===CLASS) && (instance.classNames={});
// in case of ID attributeName --> special treatment
if ((attributeName===ID) && (instance.id)) {
delete nodeids[instance.id];
delete instance.id;
}
}
else {
attrs[attributeName] = attributeValue;
// in case of STYLE attributeName --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(attributeValue);
attributeValue = extractStyle.attrStyle;
if (attributeValue) {
attrs.style = attributeValue;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attributeName --> special treatment
extractClass = extractor.extractClass(attributeValue);
attributeValue = extractClass.attrClass;
if (attributeValue) {
attrs[CLASS] = attributeValue;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (instance.id!==attributeValue) && (delete nodeids[instance.id]);
instance.id = attributeValue;
nodeids[attributeValue] = domNode;
}
}
}
return domNode;
},
/**
* Returns the vnode's style in a serialized form: the way it appears in the dom.
*
* @method serializeStyles
* @return {String} vnode's style
* @since 0.0.1
*/
serializeStyles: function() {
return extractor.serializeStyles(this.styles);
},
/**
* Sets the vnode's and dom-nodes inner HTML.
*
* Syncs with the dom. Can be invoked multiple times without issues.
*
* @method setHTML
* @param content {String} the innerHTML
* @param [suppressItagRender] {Boolean} to suppress Itags from rendering
* @param [allowScripts=false] {Boolean} whether scripts are allowed --> these should be defined with `xscript` instead of `script`
* @chainable
* @since 0.0.1
*/
setHTML: function(content, suppressItagRender, allowScripts) {
var instance = this;
instance._setChildNodes(htmlToVNodes(content, vNodeProto, instance.ns, null, suppressItagRender, allowScripts), suppressItagRender);
return instance;
},
/**
* Syncs the vnode's nodeid (if available) inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom. Can be invoked multiple times without issues.
*
* @method storeId
* @chainable
* @since 0.0.1
*/
storeId: function() {
// store node/vnode inside WeakMap:
var instance = this;
instance.id ? (nodeids[instance.id]=instance.domNode) : (delete nodeids[instance.id]);
return instance;
},
//---- private ------------------------------------------------------------------
_addToTaglist: function() {
var instance = this,
itagList;
if (instance.isItag) {
itagList = DOCUMENT.getItags(); // also reads the dom if the list isn't build yet
instance._data || Object.protectedProp(instance, '_data', {});
if (!instance._data.ce_destroyed && !itagList.contains(instance.domNode)) {
itagList.push(instance.domNode);
}
}
},
/**
* Adds a vnode to the end of the list of vChildNodes.
*
* Syncs with the DOM.
*
* @method _appendChild
* @param VNode {vnode} vnode to append
* @private
* @return {Node} the Node that was appended
* @since 0.0.1
*/
_appendChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
size, noProccessScript, scriptContent;
if ((VNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (VNode.attrs && VNode.attrs.src) ? VNode.attrs.src : VNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
VNode._moveToParent(instance);
instance.domNode._appendChild(domNode);
if (VNode.nodeType===3) {
size = instance.vChildNodes.length;
instance._normalize();
// if the size changed, then the domNode was merged
(size===instance.vChildNodes.length) || (domNode=instance.vChildNodes[instance.vChildNodes.length-1].domNode);
}
if (VNode.nodeType===1) {
VNode._addToTaglist();
VNode._emit(EV_INSERTED);
}
return domNode;
}
},
/**
* Removes the vnode from its parent vChildNodes- and vChildren-list.
*
* Does NOT sync with the dom.
*
* @method _deleteFromParent
* @private
* @chainable
* @since 0.0.1
*/
_deleteFromParent: function() {
var instance = this,
vParent = instance.vParent;
if (vParent && vParent.vChildNodes) {
vParent.vChildNodes.remove(instance);
// force to recalculate the vChildren on a next call:
(instance.nodeType===1) && (vParent._vChildren=null);
}
return instance;
},
/**
* Cleans up (empties) vnode's `_data`
*
* @method _cleanData
* @private
* @chainable
* @since 0.0.1
*/
_cleanData: function() {
var instance = this,
data = instance._data;
data && data.each(
function(value, key) {
delete data[key];
}
);
return instance;
},
/**
* Cleans up (removes) duplicated `style` definitions.
*
* @method _cleanupStyle
* @private
* @chainable
* @since 0.0.1
*/
_cleanupStyle: function() {
var instance = this,
compare = [],
removal = [],
vChildren = instance.vChildren,
len = vChildren.length,
vChild, i, styleContent, vChildInner;
for (i=0; i<len; i++) {
vChild = vChildren[i];
if (vChild.tag==='STYLE') {
vChildInner = vChild.vChildNodes[0];
if (vChildInner) {
styleContent = vChildInner.text;
if (compare.contains(styleContent)) {
removal[removal.length] = vChild;
}
else {
compare[compare.length] = styleContent;
}
}
else {
// empty style-tag
removal[removal.length] = vChild;
}
}
}
removal.forEach(function(vnode) {
instance._removeChild(vnode);
});
},
/**
* Destroys the vnode and all its vnode-vChildNodes.
* Removes it from its vParent.vChildNodes list,
* also removes its definitions inside `NS-vdom.nodeids`.
*
* Does NOT sync with the dom.
*
* @method _destroy
* @private
* @chainable
* @since 0.0.1
*/
_destroy: function(silent) {
var instance = this,
vChildNodes = instance.vChildNodes,
len, i, vChildNode, vParent, treeNodes;
if (!instance.destroyed) {
if (!silent) {
// Because we don't want to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
later(function() {
instance._emit(EV_REMOVED, null, null, null, true);
}, 5);
}
Object.protectedProp(instance, 'destroyed', true);
// first: determine the dom-tree, which module `event-dom` needs to determine where the node was before it was destroyed:
treeNodes = [instance];
vParent = instance.vParent;
while (vParent) {
treeNodes[treeNodes.length] = vParent;
vParent = vParent.vParent;
}
// mark all its vChildNodes so we can see if the node is in the DOM
_markRemoved(instance);
// if vnode is part of DOCUMENT._itagList then remove it
if (DOCUMENT._itagList && instance.isItag) {
DOCUMENT._itagList.remove(instance.domNode);
}
instance._scripts && (instance._scripts.length=0);
// The definite cleanup needs to be done after a timeout:
// someone might need to handle the Element when removed (fe to cleanup specific things)
later(function() {
instance._cleanData();
if (instance.nodeType===1) {
// _destroy all its vChildNodes
if (vChildNodes) {
len = vChildNodes.length;
for (i=0; i < len; i++) {
vChildNode = vChildNodes[i];
vChildNode && vChildNode._destroy(true);
}
}
}
instance._vChildren = null;
// explicitely set instance.domNode._vnode and instance.domNode to null in order to prevent problems with the GC (we break the circular reference)
delete instance.domNode._vnode;
// if valid id, then _remove the DOMnodeRef from internal hash
instance.id && delete nodeids[instance.id];
}, silent ? 0 : DESTROY_DELAY);
instance._deleteFromParent();
// Do not make domNode `null` --> it could be used even when not in the dom
}
return instance;
},
_emit: function(evt, attribute, newValue, prevValue, destroyEvt) {
/**
* Emitted by every Element that gets inserted.
*
* @event nodeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets removed.
*
* @event noderemove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets its content changed (innerHTML/innerText).
*
* @event nodecontentchange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute inserted.
*
* @event attributeinsert
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* </ul>
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute removed.
*
* @event attributeremove
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Strings of the attributeNames that are removed
* @since 0.1
*/
/**
* Emitted by every Element that gets an attribute changed.
*
* @event attributechange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.changed {Array} Array with Objects having three properties:
* <ul>
* <li>attribute</li>
* <li>newValue</li>
* <li>prevValue</li>
* </ul>
* @since 0.1
*/
var instance = this,
silent, attrMutations, mutationEvents, mutation, vParent;
if (!DOCUMENT.hasMutationSubs || (instance.nodeType!==1)) {
return;
}
silent = !!DOCUMENT._suppressMutationEvents;
if (!silent && (destroyEvt || !instance.destroyed)) {
// Because we don't wannt to hold down UI-experience (many descendant nodes may be removed),
// we generate EV_REMOVED emission in a future eventcycle:
mutationEvents = MUTATION_EVENTS.get(instance) || {};
if (attribute) {
attrMutations = mutationEvents[evt] || [];
if (evt===EV_ATTRIBUTE_REMOVED) {
mutation = attribute;
}
else {
mutation = {
attribute: attribute
};
if ((evt===EV_ATTRIBUTE_INSERTED) || (evt===EV_ATTRIBUTE_CHANGED)) {
mutation.newValue = newValue;
}
if ((evt===EV_ATTRIBUTE_CHANGED) && prevValue) {
mutation.prevValue = prevValue;
}
}
attrMutations.push(mutation);
mutationEvents[evt] = attrMutations;
}
else {
mutationEvents[evt] = true;
}
MUTATION_EVENTS.set(instance, mutationEvents);
// now set all parent to have a nodecontentchange:
vParent = instance.vParent;
vParent && vParent._emit(EV_CONTENT_CHANGE);
// in case of removal we need to emit EV_REMOVED for all children right now
// for they will be actually removed silently after a delay of 1 minute
(evt===EV_REMOVED) && _emitDestroyChildren(instance);
if (!BATCH_WILL_RUN) {
BATCH_WILL_RUN = true;
async(function() {
_batchEmit();
});
}
}
return instance;
},
/**
* Inserts `newVNode` before `refVNode`.
*
* Syncs with the DOM.
*
* @method _insertBefore
* @param newVNode {vnode} vnode to insert
* @param refVNode {vnode} The vnode before which newVNode should be inserted.
* @private
* @return {Node} the Node being inserted (equals domNode)
* @since 0.0.1
*/
_insertBefore: function(newVNode, refVNode) {
var instance = this,
domNode = newVNode.domNode,
index = instance.vChildNodes.indexOf(refVNode),
noProccessScript, scriptContent;
if ((newVNode.tag==='SCRIPT') && instance._scripts) {
// first check if the script already ran:
scriptContent = (newVNode.attrs && newVNode.attrs.src) ? newVNode.attrs.src : newVNode.vChildNodes[0].text;
instance._scripts.some(function(script) {
noProccessScript = (scriptContent===script);
return noProccessScript;
});
}
if (!noProccessScript) {
if (index!==-1) {
newVNode._moveToParent(instance, index);
instance.domNode._insertBefore(domNode, refVNode.domNode);
(newVNode.nodeType===3) && instance._normalize();
if (newVNode.nodeType===1) {
newVNode._addToTaglist();
newVNode._emit(EV_INSERTED);
}
return domNode;
}
else {
console.warn('trying to insert before, but no ref-node found. Will append the new node');
return instance._appendChild(newVNode);
}
}
},
/**
* Moves the vnode from its current parent.vChildNodes list towards a new parent vnode at the specified position.
*
* Does NOT sync with the dom.
*
* @method _moveToParent
* @param parentVNode {vnode} the parent-vnode
* @param [index] {Number} the position of the child. When not specified, it will be appended.
* @private
* @chainable
* @since 0.0.1
*/
_moveToParent: function(parentVNode, index) {
var instance = this,
vParent = instance.vParent;
instance._deleteFromParent();
instance.vParent = parentVNode;
parentVNode.vChildNodes || (parentVNode.vChildNodes=[]);
(typeof index==='number') ? parentVNode.vChildNodes.insertAt(instance, index) : (parentVNode.vChildNodes[parentVNode.vChildNodes.length]=instance);
// force to recalculate the vChildren on a next call:
vParent && (instance.nodeType===1) && (vParent._vChildren = null);
// force to recalculate the vChildren on a next call:
parentVNode && (instance.nodeType===1) && (parentVNode._vChildren=null);
return instance;
},
/**
* Removes empty TextNodes and merges following TextNodes inside the vnode.
*
* Syncs with the dom.
*
* @method _normalize
* @private
* @chainable
* @since 0.0.1
*/
_normalize: function() {
var instance = this,
domNode = instance.domNode,
vChildNodes = instance.vChildNodes,
changed = false,
i, preChildNode, vChildNode;
if (!instance._unNormalizable && vChildNodes) {
for (i=vChildNodes.length-1; i>=0; i--) {
vChildNode = vChildNodes[i];
preChildNode = vChildNodes[i-1]; // i will get the value `-1` eventually, which leads into undefined preChildNode
if (vChildNode.nodeType===3) {
if (vChildNode.text==='') {
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
else if (preChildNode && preChildNode.nodeType===3) {
preChildNode.text += vChildNode.text;
preChildNode.domNode.nodeValue = unescapeEntities(preChildNode.text);
_tryRemoveDomNode(domNode, vChildNode.domNode);
vChildNode._destroy();
changed = true;
}
}
}
}
changed && instance._emit(EV_CONTENT_CHANGE);
return instance;
},
/**
* Makes the vnode `normalizable`. Could be set to `false` when batch-inserting nodes, while `normalizaing` manually at the end.
* Afterwards, you should always reset `normalizable` to true.
*
* @method _normalizable
* @param value {Boolean} whether the vnode should be normalisable.
* @private
* @chainable
* @since 0.0.1
*/
_normalizable: function(value) {
var instance = this;
value ? (delete instance._unNormalizable) : (instance._unNormalizable=true);
return instance;
},
/**
* Prevents MutationObserver from making the dom sync with the vnode.
* Should be used when manipulating the dom from within the vnode itself (to preventing looping)
*
* @method _noSync
* @chainable
* @private
* @since 0.0.1
*/
_noSync: function() {
var instance = this;
if (!instance._nosync) {
instance._nosync = true;
async(function() {
instance._nosync = false;
});
}
return instance;
},
/**
* Removes the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _removeAttr
* @param attributeName {String}
* @private
* @chainable
* @since 0.0.1
*/
_removeAttr: function(attributeName, suppressItagRender) {
var instance = this,
attributeNameSplitted, ns;
if (instance.isItag && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
console.warn('Not allowed to remove the attribute '+attributeName);
return instance;
}
if (instance.attrs[attributeName]!==undefined) {
delete instance.attrs[attributeName];
// in case of STYLE attribute --> special treatment
(attributeName===STYLE) && (instance.styles={});
// in case of CLASS attribute --> special treatment
(attributeName===CLASS) && (instance.classNames={});
if (attributeName===ID) {
delete nodeids[instance.id];
delete instance.id;
}
instance._emit(EV_ATTRIBUTE_REMOVED, attributeName);
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
instance.domNode._removeAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName);
}
else {
instance.domNode._removeAttribute(attributeName);
}
}
return instance;
},
/**
* Removes the vnode's child-vnode from its vChildren and the DOM.
*
* Syncs with the DOM.
*
* @method removeChild
* @param VNode {vnode} the child-vnode to remove
* @private
* @since 0.0.1
*/
_removeChild: function(VNode) {
var instance = this,
domNode = VNode.domNode,
hadFocus = domNode && domNode.hasFocus && domNode.hasFocus() && (VNode.attrs['fm-lastitem']==='true'),
parentVNode = VNode.vParent;
VNode._destroy();
_tryRemoveDomNode(instance.domNode, VNode.domNode);
instance._normalize();
// now, reset the focus on focusmanager when needed:
if (hadFocus) {
while (parentVNode && !parentVNode.attrs['fm-manage']) {
parentVNode = parentVNode.vParent;
}
parentVNode && parentVNode.domNode.focus();
}
},
/**
* Replaces the current vnode at the parent.vChildNode list by `newVNode`
*
* Does NOT sync with the dom.
*
* @method _replaceAtParent
* @param newVNode {Object} the new vnode which should take over the place of the current vnode
* @private
* @chainable
* @since 0.0.1
*/
_replaceAtParent: function(newVNode) {
var instance = this,
vParent = instance.vParent,
vChildNodes, index;
if (vParent && (vChildNodes=vParent.vChildNodes)) {
index = vChildNodes.indexOf(instance);
// force to recalculate the vChildren on a next call:
((instance.nodeType===1) || (newVNode.nodeType===1)) && (instance.vParent._vChildren=null);
vChildNodes[index] = newVNode;
}
return instance._destroy();
},
/**
* Sets the attribute of both the vnode as well as its related dom-node.
*
* Syncs with the dom.
*
* @method _setAttr
* @param attributeName {String}
* @param value {String} the value for the attributeName
* @param [force=false] {Boolean} force the attribute to be set, even if restrictions would deny it
* @private
* @chainable
* @since 0.0.1
*/
_setAttr: function(attributeName, value, force, suppressItagRender) {
var instance = this,
extractStyle, extractClass,
attrs = instance.attrs,
prevVal = attrs[attributeName],
domNode = instance.domNode,
attributeNameSplitted, ns;
if (instance.isItag && !force && !suppressItagRender && ((instance._unchangableAttrs && instance._unchangableAttrs[attributeName]) || ((attributeName.length===2) && (attributeName.toLowerCase()==='is')))) {
if (prevVal!=value) {
console.warn('Not allowed to set the attribute '+attributeName);
}
return instance;
}
// don't check by !== --> value isn't parsed into a String yet
if (prevVal && ((value===undefined) || (value===null))) {
instance._removeAttr(attributeName, suppressItagRender);
return instance;
}
// attribute-values are always Strings:
value = String(value);
// attribute-values will be stored without " or '
value = value.replace(/"/g, '"').replace(/'/g, "'");
if (prevVal!=value) {
attrs[attributeName] = value;
// in case of STYLE attribute --> special treatment
if (attributeName===STYLE) {
extractStyle = extractor.extractStyle(value);
value = extractStyle.attrStyle;
if (value) {
attrs.style = value;
}
else {
delete attrs.style;
}
instance.styles = extractStyle.styles;
}
else if (attributeName===CLASS) {
// in case of CLASS attribute --> special treatment
extractClass = extractor.extractClass(value);
value = extractClass.attrClass;
if (value) {
attrs[CLASS] = value;
}
else {
delete attrs[CLASS];
}
instance.classNames = extractClass.classNames;
}
else if (attributeName===ID) {
instance.id && (delete nodeids[instance.id]);
instance.id = value;
nodeids[value] = domNode;
}
instance._emit(prevVal ? EV_ATTRIBUTE_CHANGED : EV_ATTRIBUTE_INSERTED, attributeName, value, prevVal);
// when set in the dom --> quotes need to be set as "
value = value.replace(/"/g, '"');
if (attributeName.indexOf(':')!==-1) {
attributeNameSplitted = attributeName.split(':');
ns = attributeNameSplitted[0];
attributeName = attributeNameSplitted[1];
domNode._setAttributeNS(xmlNS[ns.toUpperCase()] || ns, attributeName, value);
}
else {
domNode._setAttribute(attributeName, value);
}
}
return instance;
},
/**
* Redefines the attributes of both the vnode as well as its related dom-node. The new
* definition replaces any previous attributes (without touching unmodified attributes).
* the `is` attribute cannot be changed for itags.
*
* Syncs the new vnode's attributes with the dom.
*
* @method _setAttrs
* @param newAttrs {Object|Array} the new attributes to be set
* @private
* @chainable
* @since 0.0.1
*/
_setAttrs: function(newAttrs, suppressItagRender) {
// does sync the DOM
var instance = this,
attrsObj, attr, attrs, i, key, keys, len, value;
if (instance.nodeType!==1) {
return;
}
instance._noSync();
attrs = instance.attrs;
attrs.id && (delete nodeids[attrs.id]);
if (Object.isObject(newAttrs)) {
attrsObj = newAttrs;
}
else {
attrsObj = {};
len = newAttrs.length;
for (i=0; i<len; i++) {
attr = newAttrs[i];
attrsObj[attr.name] = attr.value;
}
}
if (instance.isItag && !suppressItagRender) {
if (attrs.is) {
attrsObj.is = attrs.is;
}
else {
delete attrsObj.is;
delete attrsObj.Is;
delete attrsObj.iS;
delete attrsObj.IS;
}
}
// first _remove the attributes that are no longer needed.
// quickest way for object iteration: http://jsperf.com/object-keys-iteration/20
keys = Object.keys(attrs);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
attrsObj[key] || instance._removeAttr(key, suppressItagRender);
}
// next: every attribute that differs: redefine
keys = Object.keys(attrsObj);
len = keys.length;
for (i = 0; i < len; i++) {
key = keys[i];
value = attrsObj[key];
(attrs[key]===value) || instance._setAttr(key, value, suppressItagRender);
}
return instance;
},
/**
* Redefines the childNodes of both the vnode as well as its related dom-node. The new
* definition replaces any previous nodes. (without touching unmodified nodes).
*
* Syncs the new vnode's childNodes with the dom.
*
* @method _setChildNodes
* @param newVChildNodes {Array} array with vnodes which represent the new childNodes
* @private
* @chainable
* @since 0.0.1
*/
_setChildNodes: function(newVChildNodes, suppressItagRender, removeSystemElements) {
// does sync the DOM
var instance = this,
vChildNodes = instance.vChildNodes || [],
domNode = instance.domNode,
forRemoval = [],
i, oldChild, newChild, newLength, len, len2, childDomNode, nodeswitch, bkpAttrs, cleanupStyle,
process, bkpChildNodes, needNormalize, prevSuppress, scriptContent, _scripts, scriptLen, j;
instance._noSync();
// first: reset ._vChildren --> by making it empty, its getter will refresh its list on a next call
instance._vChildren = null;
// if newVChildNodes is undefined, then we assume it to be empty --> an empty array
newVChildNodes || (newVChildNodes=[]);
// quickest way to loop through array is by using for loops: http://jsperf.com/array-foreach-vs-for-loop/5
len = vChildNodes.length;
// we need to add the systemNodes -if any- to the new newVChildNodes: they should be retained.
// SystemNodes are always places at the beginning, so they won't get reshuffled:
if (!removeSystemElements) {
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
if (oldChild._systemNode) {
newVChildNodes.insertAt(oldChild, i);
}
else {
break;
}
}
}
newLength = newVChildNodes.length;
for (i=0; i<len; i++) {
oldChild = vChildNodes[i];
childDomNode = oldChild.domNode;
if (i < newLength) {
newChild = newVChildNodes[i];
newChild.vParent || (newChild.vParent=instance);
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
/*jshint boss:true */
switch (nodeswitch=NODESWITCH[oldChild.nodeType][newChild.nodeType]) {
/*jshint boss:false */
case 1: // oldNodeType==Element, newNodeType==Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
if ((oldChild.tag!==newChild.tag) ||
((oldChild.tag===newChild.tag) && oldChild.isItag && (oldChild.attrs.is!==newChild.attrs.is))) {
// new tag --> completely replace
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
/*jshint proto:true */
oldChild.isItag && oldChild.domNode.destroyUI && oldChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
newChild.attrs = {}; // reset to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
oldChild._replaceAtParent(newChild);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
else {
// same tag --> only update what is needed
// NOTE: when this._unchangableAttrs exists, an itag-element syncs its UI -->
if (oldChild._data) {
// we might need to set the class `itag-rendered` when the attributeData says so:
// this happens when an itag gets refreshed with an unrendered definition
if (oldChild._data.itagRendered && !newChild.hasClass('itag-rendered')) {
newChild.classNames['itag-rendered'] = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' '+'itag-rendered';
}
else {
newChild.attrs[CLASS] = 'itag-rendered';
}
}
// we might need to set the class `focussed` when the attributeData says so:
// this happens when an itag gets rerendered: its renderFn doesn't know if any elements
// were focussed
if (oldChild._data.focussed && !newChild.hasClass('focussed')) {
newChild.classNames.focussed = true;
if (newChild.attrs[CLASS]) {
newChild.attrs[CLASS] = newChild.attrs[CLASS] + ' ' + 'focussed';
}
else {
newChild.attrs[CLASS] = 'focussed';
}
}
if (oldChild._data['fm-tabindex']) {
// node has the tabindex set by the focusmanager,
// but that info might got lost with re-rendering of the new element
newChild.attrs.tabindex = '0';
}
}
if (oldChild.isItag) {
prevSuppress = DOCUMENT._suppressMutationEvents || false;
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(true);
/*jshint proto:true */
newChild.domNode.destroyUI && newChild.domNode.destroyUI(PROTO_SUPPORTED ? null : newChild.__proto__.constructor);
/*jshint proto:false */
oldChild._setAttrs(newChild.attrs, suppressItagRender);
newChild._destroy(true); // destroy through the vnode and removing from DOCUMENT._itagList
DOCUMENT.suppressMutationEvents && DOCUMENT.suppressMutationEvents(prevSuppress);
}
else {
oldChild._setAttrs(newChild.attrs, suppressItagRender);
// next: sync the vChildNodes:
oldChild._setChildNodes(newChild.vChildNodes, suppressItagRender);
}
// reset ref. to the domNode, for it might have been changed by newChild:
oldChild.id && (nodeids[oldChild.id]=childDomNode);
newVChildNodes[i] = oldChild;
}
}
break;
case 2: // oldNodeType==Element, newNodeType==TextNode
// case2 and case3 should be treated the same
case 3: // oldNodeType==Element, newNodeType==Comment
oldChild.attrs.id && (delete nodeids[oldChild.attrs.id]);
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = instance;
oldChild._replaceAtParent(newChild);
instance._emit(EV_CONTENT_CHANGE);
break;
case 4: // oldNodeType==TextNode, newNodeType==Element
// case4 and case7 should be treated the same
case 7: // oldNodeType==Comment, newNodeType==Element
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild.id && (nodeids[newChild.id]=newChild.domNode);
// oldChild.isVoid = newChild.isVoid;
// delete oldChild.text;
instance._emit(EV_CONTENT_CHANGE);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
break;
case 5: // oldNodeType==TextNode, newNodeType==TextNode
// case5 and case9 should be treated the same
case 9: // oldNodeType==Comment, newNodeType==Comment
if (oldChild.text!==newChild.text) {
oldChild.text = newChild.text;
oldChild.domNode.nodeValue = unescapeEntities(newChild.text);
instance._emit(EV_CONTENT_CHANGE);
}
newVChildNodes[i] = oldChild;
break;
case 6: // oldNodeType==TextNode, newNodeType==Comment
// case6 and case8 should be treated the same
case 8: // oldNodeType==Comment, newNodeType==TextNode
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
_tryReplaceChild(domNode, newChild.domNode, childDomNode);
newChild.vParent = oldChild.vParent;
instance._emit(EV_CONTENT_CHANGE);
}
if ((nodeswitch===2) || (nodeswitch===5) || (nodeswitch===8)) {
needNormalize = true;
}
}
else {
// _remove previous definition
_tryRemoveDomNode(domNode, oldChild.domNode);
// the oldChild needs to be removed, however, this cannot be done right now, for it would effect the loop
// so we store it inside a hash to remove it later
forRemoval[forRemoval.length] = oldChild;
}
}
// now definitely remove marked childNodes:
len2 = forRemoval.length;
for (i=0; i<len2; i++) {
forRemoval[i]._destroy();
}
// now we add all new vChildNodes that go beyond `len`:
for (i = len; i < newLength; i++) {
newChild = newVChildNodes[i];
newChild.vParent = instance;
// in case a `style` tag is set --> we want to cleanup posible double definitions
(newChild.tag==='STYLE') && (cleanupStyle=true);
switch (newChild.nodeType) {
case 1: // Element
// Firts check if the tag is a script:
// Script-tags are removed from the dom after executed and stored inside parentVNode._scripts {Array}
// where the innerHTML is stored. They don't get re-inserted when they are already present inside this hash
// This way we prevent scripts from running multiple times
if (newChild.tag==='SCRIPT') {
scriptContent = (newChild.attrs && newChild.attrs.src) ? newChild.attrs.src : newChild.vChildNodes[0].text;
// check if the parent has this script set:
process = true;
/*jshint boss:true */
if (_scripts=instance._scripts) {
/*jshint boss:false */
scriptLen = _scripts.length;
for (j=0; process && (j<scriptLen); j++) {
process = (scriptContent!==scriptLen[j]);
}
}
if (process) {
// parent didn't had the script set: we will process
// but we need to remove the node once it is set
// also: the script's content will be stored on is parent,
// so we know can compare future comparision.
instance._scripts || (instance._scripts=[]);
instance._scripts[instance._scripts.length] = scriptContent;
}
}
else {
process = true;
}
if (process) {
bkpAttrs = newChild.attrs;
bkpChildNodes = newChild.vChildNodes;
newChild.attrs = {}; // reset, to force defined by `_setAttrs`
newChild.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
domNode._appendChild(newChild.domNode);
newChild._setAttrs(bkpAttrs, suppressItagRender);
newChild._setChildNodes(bkpChildNodes, suppressItagRender);
newChild._addToTaglist();
newChild._emit(EV_INSERTED);
}
break;
case 3: // TextNode
needNormalize = true;
// we need to break through --> no `break`
/* falls through */
default: // TextNode or CommentNode
// newChild.domNode.nodeValue = newChild.text;
newChild.domNode.nodeValue = unescapeEntities(newChild.text);
domNode._appendChild(newChild.domNode);
instance._emit(EV_CONTENT_CHANGE);
}
newChild.storeId();
}
instance.vChildNodes = newVChildNodes;
needNormalize && instance._normalize();
cleanupStyle && instance._cleanupStyle();
return instance;
},
_setUnchangableAttrs: function(unchangableObj) {
this._unchangableAttrs = unchangableObj;
}
};
//---- properties ------------------------------------------------------------------
/**
* A hash of all the `attributes` of the vnode's representing dom-node.
*
* @property attrs
* @type Object
* @since 0.0.1
*/
/**
* Hash with all the classes of the vnode. Every class represents a key, all values are set `true`.
*
* @property classNames
* @type Object
* @since 0.0.1
*/
/**
* The `id` of the vnode's representing dom-node (if any).
*
* @property id
* @type String
* @since 0.0.1
*/
/**
* Tells whether tag is a void Element. Examples are: `br`, `img` and `input`. Non-void Elements are f.e. `div` and `table`.
* For TextNodes and CommentNodes, this property is `undefined`.
*
* @property isVoid
* @type Boolean
* @since 0.0.1
*/
/**
* The `nodeType` of the vnode's representing dom-node (1===ElementNode, 3===TextNode, 8===CommentNode).
*
* @property nodeType
* @type Number
* @since 0.0.1
*/
/**
* The `tag` of the vnode's representing dom-node (allways uppercase).
*
* @property tag
* @type String
* @since 0.0.1
*/
/**
* The `content` of the vnode's representing dom-node, in case it is a TextNode or CommentNode.
* Equals dom-node.nodeValue.
*
* Is `undefined` for ElementNodes.
*
* @property text
* @type String
* @since 0.0.1
*/
/**
* Hash with all the childNodes (vnodes). vChildNodes are any kind of vnodes (nodeType===1, 3 or 8)
*
* @property vChildNodes
* @type Array
* @since 0.0.1
*/
/**
* The underlying `dom-node` that the vnode represents.
*
* @property domNode
* @type domNode
* @since 0.0.1
*/
/**
* vnode's parentNode (defined as a vnode itself).
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
Object.defineProperties(vNodeProto, {
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property innerHTML
* @type String
* @since 0.0.1
*/
innerHTML: {
get: function() {
return this.getHTML();
},
set: function(v) {
this.setHTML(v);
}
},
/**
* Gets or sets the innerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property nodeValue
* @type String
* @since 0.0.1
*/
nodeValue: {
get: function() {
var instance = this;
return ((instance.nodeType===3) || (instance.nodeType===8)) ? instance.text : null;
},
set: function(v) {
var instance = this,
newTextContent, prevTextContent;
if ((instance.nodeType===3) || (instance.nodeType===8)) {
prevTextContent = instance.domNode.textContent;
instance.domNode.textContent = v;
// set .text AFTER the dom-node is updated --> the content might be escaped!
newTextContent = instance.text = instance.domNode.textContent;
(newTextContent!==prevTextContent) && instance._emit(EV_CONTENT_CHANGE);
}
}
},
/**
* Gets or sets the outerHTML of both the vnode as well as the representing dom-node.
*
* The setter syncs with the DOM.
*
* @property outerHTML
* @type String
* @since 0.0.1
*/
outerHTML: {
get: function() {
return this.getOuterHTML();
},
set: function(v) {
var instance = this,
vParent = instance.vParent,
id = instance.attrs.id,
vnode, vnodes, bkpAttrs, bkpChildNodes, i, len, vChildNodes, isLastChildNode, index, refDomNode;
if ((instance.nodeType!==1) || !vParent) {
return;
}
instance._noSync();
vChildNodes = vParent.vChildNodes;
index = vChildNodes.indexOf(instance);
isLastChildNode = (index===(vChildNodes.length-1));
isLastChildNode || (refDomNode=vChildNodes[index+1].domNode);
vnodes = htmlToVNodes(v, vNodeProto, vParent.ns, vParent);
len = vnodes.length;
if (len>0) {
// the first vnode will replace the current instance:
vnode = vnodes[0];
if (vnode.nodeType===1) {
if (vnode.tag!==instance.tag) {
// new tag --> completely replace
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
id && (delete nodeids[id]);
vnode.attrs = {}; // reset to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset , to force defined by `_setAttrs`
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
// vnode.attrs = bkpAttrs;
// vnode.vChildNodes = bkpChildNodes;
vnode.id && (nodeids[vnode.id]=vnode.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
else {
instance._setAttrs(vnode.attrs);
instance._setChildNodes(vnode.vChildNodes);
}
}
else {
id && (delete nodeids[id]);
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
_tryReplaceChild(vParent.domNode, vnode.domNode, instance.domNode);
instance._replaceAtParent(vnode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
}
}
for (i=1; i<len; i++) {
vnode = vnodes[i];
switch (vnode.nodeType) {
case 1: // Element
bkpAttrs = vnode.attrs;
bkpChildNodes = vnode.vChildNodes;
vnode.attrs = {}; // reset, to force defined by `_setAttrs`
vnode.vChildNodes = []; // reset to current state, to force defined by `_setAttrs`
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._insertBefore(vnode.domNode, refDomNode);
vnode._addToTaglist();
vnode._emit(EV_INSERTED);
vnode._setAttrs(bkpAttrs);
vnode._setChildNodes(bkpChildNodes);
break;
default: // TextNode or CommentNode
vnode.domNode.nodeValue = unescapeEntities(vnode.text);
isLastChildNode ? vParent.domNode._appendChild(vnode.domNode) : vParent.domNode._appendChild(vnode.domNode, refDomNode);
}
vnode.storeId();
vnode._moveToParent(vParent, index+i);
}
}
},
/**
* Gets or sets the innerContent of the Node as plain text.
*
* The setter syncs with the DOM.
*
* @property textContent
* @type String
* @since 0.0.1
*/
textContent: {
get: function() {
var instance = this,
text = '',
vChildNodes = instance.vChildNodes,
len, i, vChildNode;
if (instance.nodeType===1) {
vChildNodes = instance.vChildNodes;
len = vChildNodes ? vChildNodes.length : 0;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
text += (vChildNode.nodeType===3) ? vChildNode.text : ((vChildNode.nodeType===1) ? vChildNode.textContent : '');
}
}
else {
text = instance.text;
}
return text;
},
set: function(v) {
var vnode = Object.create(vNodeProto);
vnode.domNode = DOCUMENT.createTextNode(v);
// create circular reference:
vnode.domNode._vnode = vnode;
vnode.nodeType = 3;
vnode.text = vnode.domNode.textContent;
this._setChildNodes([vnode]);
}
},
/**
* Hash with all the children (vnodes). vChildren are vnodes that have a representing dom-node that is an HtmlElement (nodeType===1)
*
* @property vChildren
* @type Array
* @since 0.0.1
*/
vChildren: {
get: function() {
var instance = this,
children = instance._vChildren,
vChildNode, vChildNodes, i, len;
vChildNodes = instance.vChildNodes;
if (vChildNodes && !children) {
children = instance._vChildren = [];
len = vChildNodes.length;
for (i=0; i<len; i++) {
vChildNode = vChildNodes[i];
(vChildNode.nodeType===1) && (children[children.length]=vChildNode);
}
}
children || (children = instance._vChildren = []);
return children;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirst
* @type vnode
* @since 0.0.1
*/
vFirst: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstChild;
}
},
/**
* Reference to the first vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vFirstChild
* @type vnode
* @since 0.0.1
*/
vFirstChild: {
get: function() {
return (this.vChildNodes && this.vChildNodes[0]) || null;
}
},
/**
* Reference to the first of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vFirstElement
* @type vnode
* @since 0.0.1
*/
vFirstElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vFirstElementChild;
}
},
/**
* Reference to the first vChild, where the related dom-node an Element (nodeType===1).
*
* @property vFirstElementChild
* @type vnode
* @since 0.0.1
*/
vFirstElementChild: {
get: function() {
return this.vChildren[0] || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLast
* @type vnode
* @since 0.0.1
*/
vLast: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastChild;
}
},
/**
* Reference to the last vChildNode, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vLastChild
* @type vnode
* @since 0.0.1
*/
vLastChild: {
get: function() {
var vChildNodes = this.vChildNodes;
return (vChildNodes && vChildNodes[vChildNodes.length-1]) || null;
}
},
/**
* Reference to the last of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vLastElement
* @type vnode
* @since 0.0.1
*/
vLastElement: {
get: function() {
var vParent = this.vParent;
if (!vParent) {
return null;
}
return vParent.vLastElementChild;
}
},
/**
* Reference to the last vChild, where the related dom-node an Element (nodeType===1).
*
* @property vLastElementChild
* @type vnode
* @since 0.0.1
*/
vLastElementChild: {
get: function() {
var vChildren = this.vChildren;
return vChildren[vChildren.length-1] || null;
}
},
/**
* the Parent vnode
*
* @property vParent
* @type vnode
* @since 0.0.1
*/
/**
* Reference to the next of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vNext
* @type vnode
* @since 0.0.1
*/
vNext: {
get: function() {
return _findNodeSibling(this, true);
}
},
/**
* Reference to the next of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vNextElement
* @type vnode
* @since 0.0.1
*/
vNextElement: {
get: function() {
return _findElementSibling(this, true);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is either an Element, TextNode or CommentNode (nodeType===1, 3 or 8).
*
* @property vPrevious
* @type vnode
* @since 0.0.1
*/
vPrevious: {
get: function() {
return _findNodeSibling(this);
}
},
/**
* Reference to the previous of sibbling vNode's, where the related dom-node is an Element(nodeType===1).
*
* @property vPreviousElement
* @type vnode
* @since 0.0.1
*/
vPreviousElement: {
get: function() {
return _findElementSibling(this);
}
}
});
return vNodeProto;
};
},{"./attribute-extractor.js":5900,"./html-parser.js":5904,"./vdom-ns.js":5906,"js-ext/extra/hashmap.js":5858,"js-ext/extra/lightmap.js":5859,"js-ext/lib/array.js":5860,"js-ext/lib/object.js":5861,"js-ext/lib/string.js":5863,"polyfill":5880,"utils/lib/timers.js":5883}],5908:[function(require,module,exports){
arguments[4][100][0].apply(exports,arguments)
},{"./partials/extend-document.js":5902,"./partials/extend-element.js":5903,"./partials/node-parser.js":5905,"js-ext/extra/hashmap.js":5858,"js-ext/lib/object.js":5861,"utils/lib/timers.js":5883}],5909:[function(require,module,exports){
module.exports=require(79)
},{"./lib/sizes.js":5910}],5910:[function(require,module,exports){
module.exports=require(80)
},{"js-ext/extra/hashmap.js":5911,"js-ext/lib/object.js":5912}],5911:[function(require,module,exports){
module.exports=require(4)
},{}],5912:[function(require,module,exports){
module.exports=require(13)
},{"js-ext/extra/hashmap.js":5911,"polyfill/polyfill-base.js":5915,"utils":5916}],5913:[function(require,module,exports){
module.exports=require(14)
},{}],5914:[function(require,module,exports){
module.exports=require(15)
},{}],5915:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5913,"./lib/window.console.js":5914}],5916:[function(require,module,exports){
module.exports=require(17)
},{"./lib/idgenerator.js":5917,"./lib/timers.js":5918}],5917:[function(require,module,exports){
module.exports=require(18)
},{"js-ext/extra/hashmap.js":5911,"polyfill/polyfill-base.js":5921}],5918:[function(require,module,exports){
module.exports=require(19)
},{"_process":5966,"polyfill/polyfill-base.js":5921}],5919:[function(require,module,exports){
module.exports=require(14)
},{}],5920:[function(require,module,exports){
module.exports=require(15)
},{}],5921:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5919,"./lib/window.console.js":5920}],5922:[function(require,module,exports){
module.exports=require(3475)
},{"_process":5966,"buffer":5955,"child_process":5954,"fs":5954,"http":5959,"https":5963,"url":5984,"xmldom":5930}],5923:[function(require,module,exports){
module.exports=require(3476)
},{"./lib/XMLHttpRequest.js":5922,"js-ext/extra/hashmap.js":5924,"js-ext/lib/array.js":5925,"polyfill/lib/window.console.js":5929,"url":5984,"xmldom":5930}],5924:[function(require,module,exports){
module.exports=require(4)
},{}],5925:[function(require,module,exports){
module.exports=require(27)
},{"polyfill/polyfill-base.js":5928}],5926:[function(require,module,exports){
module.exports=require(14)
},{}],5927:[function(require,module,exports){
module.exports=require(15)
},{}],5928:[function(require,module,exports){
module.exports=require(16)
},{"./lib/matchesselector.js":5926,"./lib/window.console.js":5927}],5929:[function(require,module,exports){
module.exports=require(15)
},{}],5930:[function(require,module,exports){
function DOMParser(options){
this.options = options ||{locator:{}};
}
DOMParser.prototype.parseFromString = function(source,mimeType){
var options = this.options;
var sax = new XMLReader();
var domBuilder = options.domBuilder || new DOMHandler();//contentHandler and LexicalHandler
var errorHandler = options.errorHandler;
var locator = options.locator;
var defaultNSMap = options.xmlns||{};
var entityMap = {'lt':'<','gt':'>','amp':'&','quot':'"','apos':"'"}
if(locator){
domBuilder.setDocumentLocator(locator)
}
sax.errorHandler = buildErrorHandler(errorHandler,domBuilder,locator);
sax.domBuilder = options.domBuilder || domBuilder;
if(/\/x?html?$/.test(mimeType)){
entityMap.nbsp = '\xa0';
entityMap.copy = '\xa9';
defaultNSMap['']= 'http://www.w3.org/1999/xhtml';
}
if(source){
sax.parse(source,defaultNSMap,entityMap);
}else{
sax.errorHandler.error("invalid document source");
}
return domBuilder.document;
}
function buildErrorHandler(errorImpl,domBuilder,locator){
if(!errorImpl){
if(domBuilder instanceof DOMHandler){
return domBuilder;
}
errorImpl = domBuilder ;
}
var errorHandler = {}
var isCallback = errorImpl instanceof Function;
locator = locator||{}
function build(key){
var fn = errorImpl[key];
if(!fn){
if(isCallback){
fn = errorImpl.length == 2?function(msg){errorImpl(key,msg)}:errorImpl;
}else{
var i=arguments.length;
while(--i){
if(fn = errorImpl[arguments[i]]){
break;
}
}
}
}
errorHandler[key] = fn && function(msg){
fn(msg+_locator(locator));
}||function(){};
}
build('warning','warn');
build('error','warn','warning');
build('fatalError','warn','warning','error');
return errorHandler;
}
/**
* +ContentHandler+ErrorHandler
* +LexicalHandler+EntityResolver2
* -DeclHandler-DTDHandler
*
* DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler
* DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2
* @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html
*/
function DOMHandler() {
this.cdata = false;
}
function position(locator,node){
node.lineNumber = locator.lineNumber;
node.columnNumber = locator.columnNumber;
}
/**
* @see org.xml.sax.ContentHandler#startDocument
* @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html
*/
DOMHandler.prototype = {
startDocument : function() {
this.document = new DOMImplementation().createDocument(null, null, null);
if (this.locator) {
this.document.documentURI = this.locator.systemId;
}
},
startElement:function(namespaceURI, localName, qName, attrs) {
var doc = this.document;
var el = doc.createElementNS(namespaceURI, qName||localName);
var len = attrs.length;
appendElement(this, el);
this.currentElement = el;
this.locator && position(this.locator,el)
for (var i = 0 ; i < len; i++) {
var namespaceURI = attrs.getURI(i);
var value = attrs.getValue(i);
var qName = attrs.getQName(i);
var attr = doc.createAttributeNS(namespaceURI, qName);
if( attr.getOffset){
position(attr.getOffset(1),attr)
}
attr.value = attr.nodeValue = value;
el.setAttributeNode(attr)
}
},
endElement:function(namespaceURI, localName, qName) {
var current = this.currentElement
var tagName = current.tagName;
this.currentElement = current.parentNode;
},
startPrefixMapping:function(prefix, uri) {
},
endPrefixMapping:function(prefix) {
},
processingInstruction:function(target, data) {
var ins = this.document.createProcessingInstruction(target, data);
this.locator && position(this.locator,ins)
appendElement(this, ins);
},
ignorableWhitespace:function(ch, start, length) {
},
characters:function(chars, start, length) {
chars = _toString.apply(this,arguments)
//console.log(chars)
if(this.currentElement && chars){
if (this.cdata) {
var charNode = this.document.createCDATASection(chars);
this.currentElement.appendChild(charNode);
} else {
var charNode = this.document.createTextNode(chars);
this.currentElement.appendChild(charNode);
}
this.locator && position(this.locator,charNode)
}
},
skippedEntity:function(name) {
},
endDocument:function() {
this.document.normalize();
},
setDocumentLocator:function (locator) {
if(this.locator = locator){// && !('lineNumber' in locator)){
locator.lineNumber = 0;
}
},
//LexicalHandler
comment:function(chars, start, length) {
chars = _toString.apply(this,arguments)
var comm = this.document.createComment(chars);
this.locator && position(this.locator,comm)
appendElement(this, comm);
},
startCDATA:function() {
//used in characters() methods
this.cdata = true;
},
endCDATA:function() {
this.cdata = false;
},
startDTD:function(name, publicId, systemId) {
var impl = this.document.implementation;
if (impl && impl.createDocumentType) {
var dt = impl.createDocumentType(name, publicId, systemId);
this.locator && position(this.locator,dt)
appendElement(this, dt);
}
},
/**
* @see org.xml.sax.ErrorHandler
* @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html
*/
warning:function(error) {
console.warn(error,_locator(this.locator));
},
error:function(error) {
console.error(error,_locator(this.locator));
},
fatalError:function(error) {
console.error(error,_locator(this.locator));
throw error;
}
}
function _locator(l){
if(l){
return '\n@'+(l.systemId ||'')+'#[line:'+l.lineNumber+',col:'+l.columnNumber+']'
}
}
function _toString(chars,start,length){
if(typeof chars == 'string'){
return chars.substr(start,length)
}else{//java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)")
if(chars.length >= start+length || start){
return new java.lang.String(chars,start,length)+'';
}
return chars;
}
}
/*
* @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html
* used method of org.xml.sax.ext.LexicalHandler:
* #comment(chars, start, length)
* #startCDATA()
* #endCDATA()
* #startDTD(name, publicId, systemId)
*
*
* IGNORED method of org.xml.sax.ext.LexicalHandler:
* #endDTD()
* #startEntity(name)
* #endEntity(name)
*
*
* @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html
* IGNORED method of org.xml.sax.ext.DeclHandler
* #attributeDecl(eName, aName, type, mode, value)
* #elementDecl(name, model)
* #externalEntityDecl(name, publicId, systemId)
* #internalEntityDecl(name, value)
* @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html
* IGNORED method of org.xml.sax.EntityResolver2
* #resolveEntity(String name,String publicId,String baseURI,String systemId)
* #resolveEntity(publicId, systemId)
* #getExternalSubset(name, baseURI)
* @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html
* IGNORED method of org.xml.sax.DTDHandler
* #notationDecl(name, publicId, systemId) {};
* #unparsedEntityDecl(name, publicId, systemId, notationName) {};
*/
"endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g,function(key){
DOMHandler.prototype[key] = function(){return null}
})
/* Private static helpers treated below as private instance methods, so don't need to add these to the public API; we might use a Relator to also get rid of non-standard public properties */
function appendElement (hander,node) {
if (!hander.currentElement) {
hander.document.appendChild(node);
} else {
hander.currentElement.appendChild(node);
}
}//appendChild and setAttributeNS are preformance key
if(typeof require == 'function'){
var XMLReader = require('./sax').XMLReader;
var DOMImplementation = exports.DOMImplementation = require('./dom').DOMImplementation;
exports.XMLSerializer = require('./dom').XMLSerializer ;
exports.DOMParser = DOMParser;
}
},{"./dom":5931,"./sax":5932}],5931:[function(require,module,exports){
/*
* DOM Level 2
* Object DOMException
* @see http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html
* @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html
*/
function copy(src,dest){
for(var p in src){
dest[p] = src[p];
}
}
/**
^\w+\.prototype\.([_\w]+)\s*=\s*((?:.*\{\s*?[\r\n][\s\S]*?^})|\S.*?(?=[;\r\n]));?
^\w+\.prototype\.([_\w]+)\s*=\s*(\S.*?(?=[;\r\n]));?
*/
function _extends(Class,Super){
var pt = Class.prototype;
if(Object.create){
var ppt = Object.create(Super.prototype)
pt.__proto__ = ppt;
}
if(!(pt instanceof Super)){
function t(){};
t.prototype = Super.prototype;
t = new t();
copy(pt,t);
Class.prototype = pt = t;
}
if(pt.constructor != Class){
if(typeof Class != 'function'){
console.error("unknow Class:"+Class)
}
pt.constructor = Class
}
}
var htmlns = 'http://www.w3.org/1999/xhtml' ;
// Node Types
var NodeType = {}
var ELEMENT_NODE = NodeType.ELEMENT_NODE = 1;
var ATTRIBUTE_NODE = NodeType.ATTRIBUTE_NODE = 2;
var TEXT_NODE = NodeType.TEXT_NODE = 3;
var CDATA_SECTION_NODE = NodeType.CDATA_SECTION_NODE = 4;
var ENTITY_REFERENCE_NODE = NodeType.ENTITY_REFERENCE_NODE = 5;
var ENTITY_NODE = NodeType.ENTITY_NODE = 6;
var PROCESSING_INSTRUCTION_NODE = NodeType.PROCESSING_INSTRUCTION_NODE = 7;
var COMMENT_NODE = NodeType.COMMENT_NODE = 8;
var DOCUMENT_NODE = NodeType.DOCUMENT_NODE = 9;
var DOCUMENT_TYPE_NODE = NodeType.DOCUMENT_TYPE_NODE = 10;
var DOCUMENT_FRAGMENT_NODE = NodeType.DOCUMENT_FRAGMENT_NODE = 11;
var NOTATION_NODE = NodeType.NOTATION_NODE = 12;
// ExceptionCode
var ExceptionCode = {}
var ExceptionMessage = {};
var INDEX_SIZE_ERR = ExceptionCode.INDEX_SIZE_ERR = ((ExceptionMessage[1]="Index size error"),1);
var DOMSTRING_SIZE_ERR = ExceptionCode.DOMSTRING_SIZE_ERR = ((ExceptionMessage[2]="DOMString size error"),2);
var HIERARCHY_REQUEST_ERR = ExceptionCode.HIERARCHY_REQUEST_ERR = ((ExceptionMessage[3]="Hierarchy request error"),3);
var WRONG_DOCUMENT_ERR = ExceptionCode.WRONG_DOCUMENT_ERR = ((ExceptionMessage[4]="Wrong document"),4);
var INVALID_CHARACTER_ERR = ExceptionCode.INVALID_CHARACTER_ERR = ((ExceptionMessage[5]="Invalid character"),5);
var NO_DATA_ALLOWED_ERR = ExceptionCode.NO_DATA_ALLOWED_ERR = ((ExceptionMessage[6]="No data allowed"),6);
var NO_MODIFICATION_ALLOWED_ERR = ExceptionCode.NO_MODIFICATION_ALLOWED_ERR = ((ExceptionMessage[7]="No modification allowed"),7);
var NOT_FOUND_ERR = ExceptionCode.NOT_FOUND_ERR = ((ExceptionMessage[8]="Not found"),8);
var NOT_SUPPORTED_ERR = ExceptionCode.NOT_SUPPORTED_ERR = ((ExceptionMessage[9]="Not supported"),9);
var INUSE_ATTRIBUTE_ERR = ExceptionCode.INUSE_ATTRIBUTE_ERR = ((ExceptionMessage[10]="Attribute in use"),10);
//level2
var INVALID_STATE_ERR = ExceptionCode.INVALID_STATE_ERR = ((ExceptionMessage[11]="Invalid state"),11);
var SYNTAX_ERR = ExceptionCode.SYNTAX_ERR = ((ExceptionMessage[12]="Syntax error"),12);
var INVALID_MODIFICATION_ERR = ExceptionCode.INVALID_MODIFICATION_ERR = ((ExceptionMessage[13]="Invalid modification"),13);
var NAMESPACE_ERR = ExceptionCode.NAMESPACE_ERR = ((ExceptionMessage[14]="Invalid namespace"),14);
var INVALID_ACCESS_ERR = ExceptionCode.INVALID_ACCESS_ERR = ((ExceptionMessage[15]="Invalid access"),15);
function DOMException(code, message) {
if(message instanceof Error){
var error = message;
}else{
error = this;
Error.call(this, ExceptionMessage[code]);
this.message = ExceptionMessage[code];
if(Error.captureStackTrace) Error.captureStackTrace(this, DOMException);
}
error.code = code;
if(message) this.message = this.message + ": " + message;
return error;
};
DOMException.prototype = Error.prototype;
copy(ExceptionCode,DOMException)
/**
* @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177
* The NodeList interface provides the abstraction of an ordered collection of nodes, without defining or constraining how this collection is implemented. NodeList objects in the DOM are live.
* The items in the NodeList are accessible via an integral index, starting from 0.
*/
function NodeList() {
};
NodeList.prototype = {
/**
* The number of nodes in the list. The range of valid child node indices is 0 to length-1 inclusive.
* @standard level1
*/
length:0,
/**
* Returns the indexth item in the collection. If index is greater than or equal to the number of nodes in the list, this returns null.
* @standard level1
* @param index unsigned long
* Index into the collection.
* @return Node
* The node at the indexth position in the NodeList, or null if that is not a valid index.
*/
item: function(index) {
return this[index] || null;
}
};
function LiveNodeList(node,refresh){
this._node = node;
this._refresh = refresh
_updateLiveList(this);
}
function _updateLiveList(list){
var inc = list._node._inc || list._node.ownerDocument._inc;
if(list._inc != inc){
var ls = list._refresh(list._node);
//console.log(ls.length)
__set__(list,'length',ls.length);
copy(ls,list);
list._inc = inc;
}
}
LiveNodeList.prototype.item = function(i){
_updateLiveList(this);
return this[i];
}
_extends(LiveNodeList,NodeList);
/**
*
* Objects implementing the NamedNodeMap interface are used to represent collections of nodes that can be accessed by name. Note that NamedNodeMap does not inherit from NodeList; NamedNodeMaps are not maintained in any particular order. Objects contained in an object implementing NamedNodeMap may also be accessed by an ordinal index, but this is simply to allow convenient enumeration of the contents of a NamedNodeMap, and does not imply that the DOM specifies an order to these Nodes.
* NamedNodeMap objects in the DOM are live.
* used for attributes or DocumentType entities
*/
function NamedNodeMap() {
};
function _findNodeIndex(list,node){
var i = list.length;
while(i--){
if(list[i] === node){return i}
}
}
function _addNamedNode(el,list,newAttr,oldAttr){
if(oldAttr){
list[_findNodeIndex(list,oldAttr)] = newAttr;
}else{
list[list.length++] = newAttr;
}
if(el){
newAttr.ownerElement = el;
var doc = el.ownerDocument;
if(doc){
oldAttr && _onRemoveAttribute(doc,el,oldAttr);
_onAddAttribute(doc,el,newAttr);
}
}
}
function _removeNamedNode(el,list,attr){
var i = _findNodeIndex(list,attr);
if(i>=0){
var lastIndex = list.length-1
while(i<lastIndex){
list[i] = list[++i]
}
list.length = lastIndex;
if(el){
var doc = el.ownerDocument;
if(doc){
_onRemoveAttribute(doc,el,attr);
attr.ownerElement = null;
}
}
}else{
throw DOMException(NOT_FOUND_ERR,new Error())
}
}
NamedNodeMap.prototype = {
length:0,
item:NodeList.prototype.item,
getNamedItem: function(key) {
// if(key.indexOf(':')>0 || key == 'xmlns'){
// return null;
// }
var i = this.length;
while(i--){
var attr = this[i];
if(attr.nodeName == key){
return attr;
}
}
},
setNamedItem: function(attr) {
var el = attr.ownerElement;
if(el && el!=this._ownerElement){
throw new DOMException(INUSE_ATTRIBUTE_ERR);
}
var oldAttr = this.getNamedItem(attr.nodeName);
_addNamedNode(this._ownerElement,this,attr,oldAttr);
return oldAttr;
},
/* returns Node */
setNamedItemNS: function(attr) {// raises: WRONG_DOCUMENT_ERR,NO_MODIFICATION_ALLOWED_ERR,INUSE_ATTRIBUTE_ERR
var el = attr.ownerElement, oldAttr;
if(el && el!=this._ownerElement){
throw new DOMException(INUSE_ATTRIBUTE_ERR);
}
oldAttr = this.getNamedItemNS(attr.namespaceURI,attr.localName);
_addNamedNode(this._ownerElement,this,attr,oldAttr);
return oldAttr;
},
/* returns Node */
removeNamedItem: function(key) {
var attr = this.getNamedItem(key);
_removeNamedNode(this._ownerElement,this,attr);
return attr;
},// raises: NOT_FOUND_ERR,NO_MODIFICATION_ALLOWED_ERR
//for level2
removeNamedItemNS:function(namespaceURI,localName){
var attr = this.getNamedItemNS(namespaceURI,localName);
_removeNamedNode(this._ownerElement,this,attr);
return attr;
},
getNamedItemNS: function(namespaceURI, localName) {
var i = this.length;
while(i--){
var node = this[i];
if(node.localName == localName && node.namespaceURI == namespaceURI){
return node;
}
}
return null;
}
};
/**
* @see http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490
*/
function DOMImplementation(/* Object */ features) {
this._features = {};
if (features) {
for (var feature in features) {
this._features = features[feature];
}
}
};
DOMImplementation.prototype = {
hasFeature: function(/* string */ feature, /* string */ version) {
var versions = this._features[feature.toLowerCase()];
if (versions && (!version || version in versions)) {
return true;
} else {
return false;
}
},
// Introduced in DOM Level 2:
createDocument:function(namespaceURI, qualifiedName, doctype){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR,WRONG_DOCUMENT_ERR
var doc = new Document();
doc.doctype = doctype;
if(doctype){
doc.appendChild(doctype);
}
doc.implementation = this;
doc.childNodes = new NodeList();
if(qualifiedName){
var root = doc.createElementNS(namespaceURI,qualifiedName);
doc.appendChild(root);
}
return doc;
},
// Introduced in DOM Level 2:
createDocumentType:function(qualifiedName, publicId, systemId){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR
var node = new DocumentType();
node.name = qualifiedName;
node.nodeName = qualifiedName;
node.publicId = publicId;
node.systemId = systemId;
// Introduced in DOM Level 2:
//readonly attribute DOMString internalSubset;
//TODO:..
// readonly attribute NamedNodeMap entities;
// readonly attribute NamedNodeMap notations;
return node;
}
};
/**
* @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247
*/
function Node() {
};
Node.prototype = {
firstChild : null,
lastChild : null,
previousSibling : null,
nextSibling : null,
attributes : null,
parentNode : null,
childNodes : null,
ownerDocument : null,
nodeValue : null,
namespaceURI : null,
prefix : null,
localName : null,
// Modified in DOM Level 2:
insertBefore:function(newChild, refChild){//raises
return _insertBefore(this,newChild,refChild);
},
replaceChild:function(newChild, oldChild){//raises
this.insertBefore(newChild,oldChild);
if(oldChild){
this.removeChild(oldChild);
}
},
removeChild:function(oldChild){
return _removeChild(this,oldChild);
},
appendChild:function(newChild){
return this.insertBefore(newChild,null);
},
hasChildNodes:function(){
return this.firstChild != null;
},
cloneNode:function(deep){
return cloneNode(this.ownerDocument||this,this,deep);
},
// Modified in DOM Level 2:
normalize:function(){
var child = this.firstChild;
while(child){
var next = child.nextSibling;
if(next && next.nodeType == TEXT_NODE && child.nodeType == TEXT_NODE){
this.removeChild(next);
child.appendData(next.data);
}else{
child.normalize();
child = next;
}
}
},
// Introduced in DOM Level 2:
isSupported:function(feature, version){
return this.ownerDocument.implementation.hasFeature(feature,version);
},
// Introduced in DOM Level 2:
hasAttributes:function(){
return this.attributes.length>0;
},
lookupPrefix:function(namespaceURI){
var el = this;
while(el){
var map = el._nsMap;
//console.dir(map)
if(map){
for(var n in map){
if(map[n] == namespaceURI){
return n;
}
}
}
el = el.nodeType == 2?el.ownerDocument : el.parentNode;
}
return null;
},
// Introduced in DOM Level 3:
lookupNamespaceURI:function(prefix){
var el = this;
while(el){
var map = el._nsMap;
//console.dir(map)
if(map){
if(prefix in map){
return map[prefix] ;
}
}
el = el.nodeType == 2?el.ownerDocument : el.parentNode;
}
return null;
},
// Introduced in DOM Level 3:
isDefaultNamespace:function(namespaceURI){
var prefix = this.lookupPrefix(namespaceURI);
return prefix == null;
}
};
function _xmlEncoder(c){
return c == '<' && '<' ||
c == '>' && '>' ||
c == '&' && '&' ||
c == '"' && '"' ||
'&#'+c.charCodeAt()+';'
}
copy(NodeType,Node);
copy(NodeType,Node.prototype);
/**
* @param callback return true for continue,false for break
* @return boolean true: break visit;
*/
function _visitNode(node,callback){
if(callback(node)){
return true;
}
if(node = node.firstChild){
do{
if(_visitNode(node,callback)){return true}
}while(node=node.nextSibling)
}
}
function Document(){
}
function _onAddAttribute(doc,el,newAttr){
doc && doc._inc++;
var ns = newAttr.namespaceURI ;
if(ns == 'http://www.w3.org/2000/xmlns/'){
//update namespace
el._nsMap[newAttr.prefix?newAttr.localName:''] = newAttr.value
}
}
function _onRemoveAttribute(doc,el,newAttr,remove){
doc && doc._inc++;
var ns = newAttr.namespaceURI ;
if(ns == 'http://www.w3.org/2000/xmlns/'){
//update namespace
delete el._nsMap[newAttr.prefix?newAttr.localName:'']
}
}
function _onUpdateChild(doc,el,newChild){
if(doc && doc._inc){
doc._inc++;
//update childNodes
var cs = el.childNodes;
if(newChild){
cs[cs.length++] = newChild;
}else{
//console.log(1)
var child = el.firstChild;
var i = 0;
while(child){
cs[i++] = child;
child =child.nextSibling;
}
cs.length = i;
}
}
}
/**
* attributes;
* children;
*
* writeable properties:
* nodeValue,Attr:value,CharacterData:data
* prefix
*/
function _removeChild(parentNode,child){
var previous = child.previousSibling;
var next = child.nextSibling;
if(previous){
previous.nextSibling = next;
}else{
parentNode.firstChild = next
}
if(next){
next.previousSibling = previous;
}else{
parentNode.lastChild = previous;
}
_onUpdateChild(parentNode.ownerDocument,parentNode);
return child;
}
/**
* preformance key(refChild == null)
*/
function _insertBefore(parentNode,newChild,nextChild){
var cp = newChild.parentNode;
if(cp){
cp.removeChild(newChild);//remove and update
}
if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){
var newFirst = newChild.firstChild;
if (newFirst == null) {
return newChild;
}
var newLast = newChild.lastChild;
}else{
newFirst = newLast = newChild;
}
var pre = nextChild ? nextChild.previousSibling : parentNode.lastChild;
newFirst.previousSibling = pre;
newLast.nextSibling = nextChild;
if(pre){
pre.nextSibling = newFirst;
}else{
parentNode.firstChild = newFirst;
}
if(nextChild == null){
parentNode.lastChild = newLast;
}else{
nextChild.previousSibling = newLast;
}
do{
newFirst.parentNode = parentNode;
}while(newFirst !== newLast && (newFirst= newFirst.nextSibling))
_onUpdateChild(parentNode.ownerDocument||parentNode,parentNode);
//console.log(parentNode.lastChild.nextSibling == null)
if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) {
newChild.firstChild = newChild.lastChild = null;
}
return newChild;
}
function _appendSingleChild(parentNode,newChild){
var cp = newChild.parentNode;
if(cp){
var pre = parentNode.lastChild;
cp.removeChild(newChild);//remove and update
var pre = parentNode.lastChild;
}
var pre = parentNode.lastChild;
newChild.parentNode = parentNode;
newChild.previousSibling = pre;
newChild.nextSibling = null;
if(pre){
pre.nextSibling = newChild;
}else{
parentNode.firstChild = newChild;
}
parentNode.lastChild = newChild;
_onUpdateChild(parentNode.ownerDocument,parentNode,newChild);
return newChild;
//console.log("__aa",parentNode.lastChild.nextSibling == null)
}
Document.prototype = {
//implementation : null,
nodeName : '#document',
nodeType : DOCUMENT_NODE,
doctype : null,
documentElement : null,
_inc : 1,
insertBefore : function(newChild, refChild){//raises
if(newChild.nodeType == DOCUMENT_FRAGMENT_NODE){
var child = newChild.firstChild;
while(child){
var next = child.nextSibling;
this.insertBefore(child,refChild);
child = next;
}
return newChild;
}
if(this.documentElement == null && newChild.nodeType == 1){
this.documentElement = newChild;
}
return _insertBefore(this,newChild,refChild),(newChild.ownerDocument = this),newChild;
},
removeChild : function(oldChild){
if(this.documentElement == oldChild){
this.documentElement = null;
}
return _removeChild(this,oldChild);
},
// Introduced in DOM Level 2:
importNode : function(importedNode,deep){
return importNode(this,importedNode,deep);
},
// Introduced in DOM Level 2:
getElementById : function(id){
var rtv = null;
_visitNode(this.documentElement,function(node){
if(node.nodeType == 1){
if(node.getAttribute('id') == id){
rtv = node;
return true;
}
}
})
return rtv;
},
//document factory method:
createElement : function(tagName){
var node = new Element();
node.ownerDocument = this;
node.nodeName = tagName;
node.tagName = tagName;
node.childNodes = new NodeList();
var attrs = node.attributes = new NamedNodeMap();
attrs._ownerElement = node;
return node;
},
createDocumentFragment : function(){
var node = new DocumentFragment();
node.ownerDocument = this;
node.childNodes = new NodeList();
return node;
},
createTextNode : function(data){
var node = new Text();
node.ownerDocument = this;
node.appendData(data)
return node;
},
createComment : function(data){
var node = new Comment();
node.ownerDocument = this;
node.appendData(data)
return node;
},
createCDATASection : function(data){
var node = new CDATASection();
node.ownerDocument = this;
node.appendData(data)
return node;
},
createProcessingInstruction : function(target,data){
var node = new ProcessingInstruction();
node.ownerDocument = this;
node.tagName = node.target = target;
node.nodeValue= node.data = data;
return node;
},
createAttribute : function(name){
var node = new Attr();
node.ownerDocument = this;
node.name = name;
node.nodeName = name;
node.localName = name;
node.specified = true;
return node;
},
createEntityReference : function(name){
var node = new EntityReference();
node.ownerDocument = this;
node.nodeName = name;
return node;
},
// Introduced in DOM Level 2:
createElementNS : function(namespaceURI,qualifiedName){
var node = new Element();
var pl = qualifiedName.split(':');
var attrs = node.attributes = new NamedNodeMap();
node.childNodes = new NodeList();
node.ownerDocument = this;
node.nodeName = qualifiedName;
node.tagName = qualifiedName;
node.namespaceURI = namespaceURI;
if(pl.length == 2){
node.prefix = pl[0];
node.localName = pl[1];
}else{
//el.prefix = null;
node.localName = qualifiedName;
}
attrs._ownerElement = node;
return node;
},
// Introduced in DOM Level 2:
createAttributeNS : function(namespaceURI,qualifiedName){
var node = new Attr();
var pl = qualifiedName.split(':');
node.ownerDocument = this;
node.nodeName = qualifiedName;
node.name = qualifiedName;
node.namespaceURI = namespaceURI;
node.specified = true;
if(pl.length == 2){
node.prefix = pl[0];
node.localName = pl[1];
}else{
//el.prefix = null;
node.localName = qualifiedName;
}
return node;
}
};
_extends(Document,Node);
function Element() {
this._nsMap = {};
};
Element.prototype = {
nodeType : ELEMENT_NODE,
hasAttribute : function(name){
return this.getAttributeNode(name)!=null;
},
getAttribute : function(name){
var attr = this.getAttributeNode(name);
return attr && attr.value || '';
},
getAttributeNode : function(name){
return this.attributes.getNamedItem(name);
},
setAttribute : function(name, value){
var attr = this.ownerDocument.createAttribute(name);
attr.value = attr.nodeValue = "" + value;
this.setAttributeNode(attr)
},
removeAttribute : function(name){
var attr = this.getAttributeNode(name)
attr && this.removeAttributeNode(attr);
},
//four real opeartion method
appendChild:function(newChild){
if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){
return this.insertBefore(newChild,null);
}else{
return _appendSingleChild(this,newChild);
}
},
setAttributeNode : function(newAttr){
return this.attributes.setNamedItem(newAttr);
},
setAttributeNodeNS : function(newAttr){
return this.attributes.setNamedItemNS(newAttr);
},
removeAttributeNode : function(oldAttr){
return this.attributes.removeNamedItem(oldAttr.nodeName);
},
//get real attribute name,and remove it by removeAttributeNode
removeAttributeNS : function(namespaceURI, localName){
var old = this.getAttributeNodeNS(namespaceURI, localName);
old && this.removeAttributeNode(old);
},
hasAttributeNS : function(namespaceURI, localName){
return this.getAttributeNodeNS(namespaceURI, localName)!=null;
},
getAttributeNS : function(namespaceURI, localName){
var attr = this.getAttributeNodeNS(namespaceURI, localName);
return attr && attr.value || '';
},
setAttributeNS : function(namespaceURI, qualifiedName, value){
var attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName);
attr.value = attr.nodeValue = value;
this.setAttributeNode(attr)
},
getAttributeNodeNS : function(namespaceURI, localName){
return this.attributes.getNamedItemNS(namespaceURI, localName);
},
getElementsByTagName : function(tagName){
return new LiveNodeList(this,function(base){
var ls = [];
_visitNode(base,function(node){
if(node !== base && node.nodeType == ELEMENT_NODE && (tagName === '*' || node.tagName == tagName)){
ls.push(node);
}
});
return ls;
});
},
getElementsByTagNameNS : function(namespaceURI, localName){
return new LiveNodeList(this,function(base){
var ls = [];
_visitNode(base,function(node){
if(node !== base && node.nodeType === ELEMENT_NODE && node.namespaceURI === namespaceURI && (localName === '*' || node.localName == localName)){
ls.push(node);
}
});
return ls;
});
}
};
Document.prototype.getElementsByTagName = Element.prototype.getElementsByTagName;
Document.prototype.getElementsByTagNameNS = Element.prototype.getElementsByTagNameNS;
_extends(Element,Node);
function Attr() {
};
Attr.prototype.nodeType = ATTRIBUTE_NODE;
_extends(Attr,Node);
function CharacterData() {
};
CharacterData.prototype = {
data : '',
substringData : function(offset, count) {
return this.data.substring(offset, offset+count);
},
appendData: function(text) {
text = this.data+text;
this.nodeValue = this.data = text;
this.length = text.length;
},
insertData: function(offset,text) {
this.replaceData(offset,0,text);
},
appendChild:function(newChild){
//if(!(newChild instanceof CharacterData)){
throw new Error(ExceptionMessage[3])
//}
return Node.prototype.appendChild.apply(this,arguments)
},
deleteData: function(offset, count) {
this.replaceData(offset,count,"");
},
replaceData: function(offset, count, text) {
var start = this.data.substring(0,offset);
var end = this.data.substring(offset+count);
text = start + text + end;
this.nodeValue = this.data = text;
this.length = text.length;
}
}
_extends(CharacterData,Node);
function Text() {
};
Text.prototype = {
nodeName : "#text",
nodeType : TEXT_NODE,
splitText : function(offset) {
var text = this.data;
var newText = text.substring(offset);
text = text.substring(0, offset);
this.data = this.nodeValue = text;
this.length = text.length;
var newNode = this.ownerDocument.createTextNode(newText);
if(this.parentNode){
this.parentNode.insertBefore(newNode, this.nextSibling);
}
return newNode;
}
}
_extends(Text,CharacterData);
function Comment() {
};
Comment.prototype = {
nodeName : "#comment",
nodeType : COMMENT_NODE
}
_extends(Comment,CharacterData);
function CDATASection() {
};
CDATASection.prototype = {
nodeName : "#cdata-section",
nodeType : CDATA_SECTION_NODE
}
_extends(CDATASection,CharacterData);
function DocumentType() {
};
DocumentType.prototype.nodeType = DOCUMENT_TYPE_NODE;
_extends(DocumentType,Node);
function Notation() {
};
Notation.prototype.nodeType = NOTATION_NODE;
_extends(Notation,Node);
function Entity() {
};
Entity.prototype.nodeType = ENTITY_NODE;
_extends(Entity,Node);
function EntityReference() {
};
EntityReference.prototype.nodeType = ENTITY_REFERENCE_NODE;
_extends(EntityReference,Node);
function DocumentFragment() {
};
DocumentFragment.prototype.nodeName = "#document-fragment";
DocumentFragment.prototype.nodeType = DOCUMENT_FRAGMENT_NODE;
_extends(DocumentFragment,Node);
function ProcessingInstruction() {
}
ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE;
_extends(ProcessingInstruction,Node);
function XMLSerializer(){}
XMLSerializer.prototype.serializeToString = function(node){
var buf = [];
serializeToString(node,buf);
return buf.join('');
}
Node.prototype.toString =function(){
return XMLSerializer.prototype.serializeToString(this);
}
function serializeToString(node,buf){
switch(node.nodeType){
case ELEMENT_NODE:
var attrs = node.attributes;
var len = attrs.length;
var child = node.firstChild;
var nodeName = node.tagName;
var isHTML = htmlns === node.namespaceURI
buf.push('<',nodeName);
for(var i=0;i<len;i++){
serializeToString(attrs.item(i),buf,isHTML);
}
if(child || isHTML && !/^(?:meta|link|img|br|hr|input)$/i.test(nodeName)){
buf.push('>');
//if is cdata child node
if(isHTML && /^script$/i.test(nodeName)){
if(child){
buf.push(child.data);
}
}else{
while(child){
serializeToString(child,buf);
child = child.nextSibling;
}
}
buf.push('</',nodeName,'>');
}else{
buf.push('/>');
}
return;
case DOCUMENT_NODE:
case DOCUMENT_FRAGMENT_NODE:
var child = node.firstChild;
while(child){
serializeToString(child,buf);
child = child.nextSibling;
}
return;
case ATTRIBUTE_NODE:
return buf.push(' ',node.name,'="',node.value.replace(/[<&"]/g,_xmlEncoder),'"');
case TEXT_NODE:
return buf.push(node.data.replace(/[<&]/g,_xmlEncoder));
case CDATA_SECTION_NODE:
return buf.push( '<![CDATA[',node.data,']]>');
case COMMENT_NODE:
return buf.push( "<!--",node.data,"-->");
case DOCUMENT_TYPE_NODE:
var pubid = node.publicId;
var sysid = node.systemId;
buf.push('<!DOCTYPE ',node.name);
if(pubid){
buf.push(' PUBLIC "',pubid);
if (sysid && sysid!='.') {
buf.push( '" "',sysid);
}
buf.push('">');
}else if(sysid && sysid!='.'){
buf.push(' SYSTEM "',sysid,'">');
}else{
var sub = node.internalSubset;
if(sub){
buf.push(" [",sub,"]");
}
buf.push(">");
}
return;
case PROCESSING_INSTRUCTION_NODE:
return buf.push( "<?",node.target," ",node.data,"?>");
case ENTITY_REFERENCE_NODE:
return buf.push( '&',node.nodeName,';');
//case ENTITY_NODE:
//case NOTATION_NODE:
default:
buf.push('??',node.nodeName);
}
}
function importNode(doc,node,deep){
var node2;
switch (node.nodeType) {
case ELEMENT_NODE:
node2 = node.cloneNode(false);
node2.ownerDocument = doc;
//var attrs = node2.attributes;
//var len = attrs.length;
//for(var i=0;i<len;i++){
//node2.setAttributeNodeNS(importNode(doc,attrs.item(i),deep));
//}
case DOCUMENT_FRAGMENT_NODE:
break;
case ATTRIBUTE_NODE:
deep = true;
break;
//case ENTITY_REFERENCE_NODE:
//case PROCESSING_INSTRUCTION_NODE:
////case TEXT_NODE:
//case CDATA_SECTION_NODE:
//case COMMENT_NODE:
// deep = false;
// break;
//case DOCUMENT_NODE:
//case DOCUMENT_TYPE_NODE:
//cannot be imported.
//case ENTITY_NODE:
//case NOTATION_NODE:
//can not hit in level3
//default:throw e;
}
if(!node2){
node2 = node.cloneNode(false);//false
}
node2.ownerDocument = doc;
node2.parentNode = null;
if(deep){
var child = node.firstChild;
while(child){
node2.appendChild(importNode(doc,child,deep));
child = child.nextSibling;
}
}
return node2;
}
//
//var _relationMap = {firstChild:1,lastChild:1,previousSibling:1,nextSibling:1,
// attributes:1,childNodes:1,parentNode:1,documentElement:1,doctype,};
function cloneNode(doc,node,deep){
var node2 = new node.constructor();
for(var n in node){
var v = node[n];
if(typeof v != 'object' ){
if(v != node2[n]){
node2[n] = v;
}
}
}
if(node.childNodes){
node2.childNodes = new NodeList();
}
node2.ownerDocument = doc;
switch (node2.nodeType) {
case ELEMENT_NODE:
var attrs = node.attributes;
var attrs2 = node2.attributes = new NamedNodeMap();
var len = attrs.length
attrs2._ownerElement = node2;
for(var i=0;i<len;i++){
node2.setAttributeNode(cloneNode(doc,attrs.item(i),true));
}
break;;
case ATTRIBUTE_NODE:
deep = true;
}
if(deep){
var child = node.firstChild;
while(child){
node2.appendChild(cloneNode(doc,child,deep));
child = child.nextSibling;
}
}
return node2;
}
function __set__(object,key,value){
object[key] = value
}
//do dynamic
try{
if(Object.defineProperty){
Object.defineProperty(LiveNodeList.prototype,'length',{
get:function(){
_updateLiveList(this);
return this.$$length;
}
});
Object.defineProperty(Node.prototype,'textContent',{
get:function(){
return getTextContent(this);
},
set:function(data){
switch(this.nodeType){
case 1:
case 11:
while(this.firstChild){
this.removeChild(this.firstChild);
}
if(data || String(data)){
this.appendChild(this.ownerDocument.createTextNode(data));
}
break;
default:
//TODO:
this.data = data;
this.value = value;
this.nodeValue = data;
}
}
})
function getTextContent(node){
switch(node.nodeType){
case 1:
case 11:
var buf = [];
node = node.firstChild;
while(node){
if(node.nodeType!==7 && node.nodeType !==8){
buf.push(getTextContent(node));
}
node = node.nextSibling;
}
return buf.join('');
default:
return node.nodeValue;
}
}
__set__ = function(object,key,value){
//console.log(value)
object['$$'+key] = value
}
}
}catch(e){//ie8
}
if(typeof require == 'function'){
exports.DOMImplementation = DOMImplementation;
exports.XMLSerializer = XMLSerializer;
}
},{}],5932:[function(require,module,exports){
//[4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
//[4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
//[5] Name ::= NameStartChar (NameChar)*
var nameStartChar = /[A-Z_a-z\xC0-\xD6\xD8-\xF6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]///\u10000-\uEFFFF
var nameChar = new RegExp("[\\-\\.0-9"+nameStartChar.source.slice(1,-1)+"\u00B7\u0300-\u036F\\ux203F-\u2040]");
var tagNamePattern = new RegExp('^'+nameStartChar.source+nameChar.source+'*(?:\:'+nameStartChar.source+nameChar.source+'*)?$');
//var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/
//var handlers = 'resolveEntity,getExternalSubset,characters,endDocument,endElement,endPrefixMapping,ignorableWhitespace,processingInstruction,setDocumentLocator,skippedEntity,startDocument,startElement,startPrefixMapping,notationDecl,unparsedEntityDecl,error,fatalError,warning,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,comment,endCDATA,endDTD,endEntity,startCDATA,startDTD,startEntity'.split(',')
//S_TAG, S_ATTR, S_EQ, S_V
//S_ATTR_S, S_E, S_S, S_C
var S_TAG = 0;//tag name offerring
var S_ATTR = 1;//attr name offerring
var S_ATTR_S=2;//attr name end and space offer
var S_EQ = 3;//=space?
var S_V = 4;//attr value(no quot value only)
var S_E = 5;//attr value end and no space(quot end)
var S_S = 6;//(attr value end || tag end ) && (space offer)
var S_C = 7;//closed el<el />
function XMLReader(){
}
XMLReader.prototype = {
parse:function(source,defaultNSMap,entityMap){
var domBuilder = this.domBuilder;
domBuilder.startDocument();
_copy(defaultNSMap ,defaultNSMap = {})
parse(source,defaultNSMap,entityMap,
domBuilder,this.errorHandler);
domBuilder.endDocument();
}
}
function parse(source,defaultNSMapCopy,entityMap,domBuilder,errorHandler){
function fixedFromCharCode(code) {
// String.prototype.fromCharCode does not supports
// > 2 bytes unicode chars directly
if (code > 0xffff) {
code -= 0x10000;
var surrogate1 = 0xd800 + (code >> 10)
, surrogate2 = 0xdc00 + (code & 0x3ff);
return String.fromCharCode(surrogate1, surrogate2);
} else {
return String.fromCharCode(code);
}
}
function entityReplacer(a){
var k = a.slice(1,-1);
if(k in entityMap){
return entityMap[k];
}else if(k.charAt(0) === '#'){
return fixedFromCharCode(parseInt(k.substr(1).replace('x','0x')))
}else{
errorHandler.error('entity not found:'+a);
return a;
}
}
function appendText(end){//has some bugs
var xt = source.substring(start,end).replace(/&#?\w+;/g,entityReplacer);
locator&&position(start);
domBuilder.characters(xt,0,end-start);
start = end
}
function position(start,m){
while(start>=endPos && (m = linePattern.exec(source))){
startPos = m.index;
endPos = startPos + m[0].length;
locator.lineNumber++;
//console.log('line++:',locator,startPos,endPos)
}
locator.columnNumber = start-startPos+1;
}
var startPos = 0;
var endPos = 0;
var linePattern = /.+(?:\r\n?|\n)|.*$/g
var locator = domBuilder.locator;
var parseStack = [{currentNSMap:defaultNSMapCopy}]
var closeMap = {};
var start = 0;
while(true){
var i = source.indexOf('<',start);
if(i<0){
if(!source.substr(start).match(/^\s*$/)){
var doc = domBuilder.document;
var text = doc.createTextNode(source.substr(start));
doc.appendChild(text);
domBuilder.currentElement = text;
}
return;
}
if(i>start){
appendText(i);
}
switch(source.charAt(i+1)){
case '/':
var end = source.indexOf('>',i+3);
var tagName = source.substring(i+2,end);
var config = parseStack.pop();
var localNSMap = config.localNSMap;
if(config.tagName != tagName){
errorHandler.fatalError("end tag name: "+tagName+' is not match the current start tagName:'+config.tagName );
}
domBuilder.endElement(config.uri,config.localName,tagName);
if(localNSMap){
for(var prefix in localNSMap){
domBuilder.endPrefixMapping(prefix) ;
}
}
end++;
break;
// end elment
case '?':// <?...?>
locator&&position(i);
end = parseInstruction(source,i,domBuilder);
break;
case '!':// <!doctype,<![CDATA,<!--
locator&&position(i);
end = parseDCC(source,i,domBuilder,errorHandler);
break;
default:
try{
locator&&position(i);
var el = new ElementAttributes();
//elStartEnd
var end = parseElementStartPart(source,i,el,entityReplacer,errorHandler);
var len = el.length;
//position fixed
if(len && locator){
var backup = copyLocator(locator,{});
for(var i = 0;i<len;i++){
var a = el[i];
position(a.offset);
a.offset = copyLocator(locator,{});
}
copyLocator(backup,locator);
}
if(!el.closed && fixSelfClosed(source,end,el.tagName,closeMap)){
el.closed = true;
if(!entityMap.nbsp){
errorHandler.warning('unclosed xml attribute');
}
}
appendElement(el,domBuilder,parseStack);
if(el.uri === 'http://www.w3.org/1999/xhtml' && !el.closed){
end = parseHtmlSpecialContent(source,end,el.tagName,entityReplacer,domBuilder)
}else{
end++;
}
}catch(e){
errorHandler.error('element parse error: '+e);
end = -1;
}
}
if(end<0){
//TODO: 这里有可能sax回退,有位置错误风险
appendText(i+1);
}else{
start = end;
}
}
}
function copyLocator(f,t){
t.lineNumber = f.lineNumber;
t.columnNumber = f.columnNumber;
return t;
}
/**
* @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack);
* @return end of the elementStartPart(end of elementEndPart for selfClosed el)
*/
function parseElementStartPart(source,start,el,entityReplacer,errorHandler){
var attrName;
var value;
var p = ++start;
var s = S_TAG;//status
while(true){
var c = source.charAt(p);
switch(c){
case '=':
if(s === S_ATTR){//attrName
attrName = source.slice(start,p);
s = S_EQ;
}else if(s === S_ATTR_S){
s = S_EQ;
}else{
//fatalError: equal must after attrName or space after attrName
throw new Error('attribute equal must after attrName');
}
break;
case '\'':
case '"':
if(s === S_EQ){//equal
start = p+1;
p = source.indexOf(c,start)
if(p>0){
value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
el.add(attrName,value,start-1);
s = S_E;
}else{
//fatalError: no end quot match
throw new Error('attribute value no end \''+c+'\' match');
}
}else if(s == S_V){
value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
//console.log(attrName,value,start,p)
el.add(attrName,value,start);
//console.dir(el)
errorHandler.warning('attribute "'+attrName+'" missed start quot('+c+')!!');
start = p+1;
s = S_E
}else{
//fatalError: no equal before
throw new Error('attribute value must after "="');
}
break;
case '/':
switch(s){
case S_TAG:
el.setTagName(source.slice(start,p));
case S_E:
case S_S:
case S_C:
s = S_C;
el.closed = true;
case S_V:
case S_ATTR:
case S_ATTR_S:
break;
//case S_EQ:
default:
throw new Error("attribute invalid close char('/')")
}
break;
case ''://end document
//throw new Error('unexpected end of input')
errorHandler.error('unexpected end of input');
case '>':
switch(s){
case S_TAG:
el.setTagName(source.slice(start,p));
case S_E:
case S_S:
case S_C:
break;//normal
case S_V://Compatible state
case S_ATTR:
value = source.slice(start,p);
if(value.slice(-1) === '/'){
el.closed = true;
value = value.slice(0,-1)
}
case S_ATTR_S:
if(s === S_ATTR_S){
value = attrName;
}
if(s == S_V){
errorHandler.warning('attribute "'+value+'" missed quot(")!!');
el.add(attrName,value.replace(/&#?\w+;/g,entityReplacer),start)
}else{
errorHandler.warning('attribute "'+value+'" missed value!! "'+value+'" instead!!')
el.add(value,value,start)
}
break;
case S_EQ:
throw new Error('attribute value missed!!');
}
// console.log(tagName,tagNamePattern,tagNamePattern.test(tagName))
return p;
/*xml space '\x20' | #x9 | #xD | #xA; */
case '\u0080':
c = ' ';
default:
if(c<= ' '){//space
switch(s){
case S_TAG:
el.setTagName(source.slice(start,p));//tagName
s = S_S;
break;
case S_ATTR:
attrName = source.slice(start,p)
s = S_ATTR_S;
break;
case S_V:
var value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
errorHandler.warning('attribute "'+value+'" missed quot(")!!');
el.add(attrName,value,start)
case S_E:
s = S_S;
break;
//case S_S:
//case S_EQ:
//case S_ATTR_S:
// void();break;
//case S_C:
//ignore warning
}
}else{//not space
//S_TAG, S_ATTR, S_EQ, S_V
//S_ATTR_S, S_E, S_S, S_C
switch(s){
//case S_TAG:void();break;
//case S_ATTR:void();break;
//case S_V:void();break;
case S_ATTR_S:
errorHandler.warning('attribute "'+attrName+'" missed value!! "'+attrName+'" instead!!')
el.add(attrName,attrName,start);
start = p;
s = S_ATTR;
break;
case S_E:
errorHandler.warning('attribute space is required"'+attrName+'"!!')
case S_S:
s = S_ATTR;
start = p;
break;
case S_EQ:
s = S_V;
start = p;
break;
case S_C:
throw new Error("elements closed character '/' and '>' must be connected to");
}
}
}
p++;
}
}
/**
* @return end of the elementStartPart(end of elementEndPart for selfClosed el)
*/
function appendElement(el,domBuilder,parseStack){
var tagName = el.tagName;
var localNSMap = null;
var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
var i = el.length;
while(i--){
var a = el[i];
var qName = a.qName;
var value = a.value;
var nsp = qName.indexOf(':');
if(nsp>0){
var prefix = a.prefix = qName.slice(0,nsp);
var localName = qName.slice(nsp+1);
var nsPrefix = prefix === 'xmlns' && localName
}else{
localName = qName;
prefix = null
nsPrefix = qName === 'xmlns' && ''
}
//can not set prefix,because prefix !== ''
a.localName = localName ;
//prefix == null for no ns prefix attribute
if(nsPrefix !== false){//hack!!
if(localNSMap == null){
localNSMap = {}
//console.log(currentNSMap,0)
_copy(currentNSMap,currentNSMap={})
//console.log(currentNSMap,1)
}
currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value;
a.uri = 'http://www.w3.org/2000/xmlns/'
domBuilder.startPrefixMapping(nsPrefix, value)
}
}
var i = el.length;
while(i--){
a = el[i];
var prefix = a.prefix;
if(prefix){//no prefix attribute has no namespace
if(prefix === 'xml'){
a.uri = 'http://www.w3.org/XML/1998/namespace';
}if(prefix !== 'xmlns'){
a.uri = currentNSMap[prefix]
//{console.log('###'+a.qName,domBuilder.locator.systemId+'',currentNSMap,a.uri)}
}
}
}
var nsp = tagName.indexOf(':');
if(nsp>0){
prefix = el.prefix = tagName.slice(0,nsp);
localName = el.localName = tagName.slice(nsp+1);
}else{
prefix = null;//important!!
localName = el.localName = tagName;
}
//no prefix element has default namespace
var ns = el.uri = currentNSMap[prefix || ''];
domBuilder.startElement(ns,localName,tagName,el);
//endPrefixMapping and startPrefixMapping have not any help for dom builder
//localNSMap = null
if(el.closed){
domBuilder.endElement(ns,localName,tagName);
if(localNSMap){
for(prefix in localNSMap){
domBuilder.endPrefixMapping(prefix)
}
}
}else{
el.currentNSMap = currentNSMap;
el.localNSMap = localNSMap;
parseStack.push(el);
}
}
function parseHtmlSpecialContent(source,elStartEnd,tagName,entityReplacer,domBuilder){
if(/^(?:script|textarea)$/i.test(tagName)){
var elEndStart = source.indexOf('</'+tagName+'>',elStartEnd);
var text = source.substring(elStartEnd+1,elEndStart);
if(/[&<]/.test(text)){
if(/^script$/i.test(tagName)){
//if(!/\]\]>/.test(text)){
//lexHandler.startCDATA();
domBuilder.characters(text,0,text.length);
//lexHandler.endCDATA();
return elEndStart;
//}
}//}else{//text area
text = text.replace(/&#?\w+;/g,entityReplacer);
domBuilder.characters(text,0,text.length);
return elEndStart;
//}
}
}
return elStartEnd+1;
}
function fixSelfClosed(source,elStartEnd,tagName,closeMap){
//if(tagName in closeMap){
var pos = closeMap[tagName];
if(pos == null){
//console.log(tagName)
pos = closeMap[tagName] = source.lastIndexOf('</'+tagName+'>')
}
return pos<elStartEnd;
//}
}
function _copy(source,target){
for(var n in source){target[n] = source[n]}
}
function parseDCC(source,start,domBuilder,errorHandler){//sure start with '<!'
var next= source.charAt(start+2)
switch(next){
case '-':
if(source.charAt(start + 3) === '-'){
var end = source.indexOf('-->',start+4);
//append comment source.substring(4,end)//<!--
if(end>start){
domBuilder.comment(source,start+4,end-start-4);
return end+3;
}else{
errorHandler.error("Unclosed comment");
return -1;
}
}else{
//error
return -1;
}
default:
if(source.substr(start+3,6) == 'CDATA['){
var end = source.indexOf(']]>',start+9);
domBuilder.startCDATA();
domBuilder.characters(source,start+9,end-start-9);
domBuilder.endCDATA()
return end+3;
}
//<!DOCTYPE
//startDTD(java.lang.String name, java.lang.String publicId, java.lang.String systemId)
var matchs = split(source,start);
var len = matchs.length;
if(len>1 && /!doctype/i.test(matchs[0][0])){
var name = matchs[1][0];
var pubid = len>3 && /^public$/i.test(matchs[2][0]) && matchs[3][0]
var sysid = len>4 && matchs[4][0];
var lastMatch = matchs[len-1]
domBuilder.startDTD(name,pubid && pubid.replace(/^(['"])(.*?)\1$/,'$2'),
sysid && sysid.replace(/^(['"])(.*?)\1$/,'$2'));
domBuilder.endDTD();
return lastMatch.index+lastMatch[0].length
}
}
return -1;
}
function parseInstruction(source,start,domBuilder){
var end = source.indexOf('?>',start);
if(end){
var match = source.substring(start,end).match(/^<\?(\S*)\s*([\s\S]*?)\s*$/);
if(match){
var len = match[0].length;
domBuilder.processingInstruction(match[1], match[2]) ;
return end+2;
}else{//error
return -1;
}
}
return -1;
}
/**
* @param source
*/
function ElementAttributes(source){
}
ElementAttributes.prototype = {
setTagName:function(tagName){
if(!tagNamePattern.test(tagName)){
throw new Error('invalid tagName:'+tagName)
}
this.tagName = tagName
},
add:function(qName,value,offset){
if(!tagNamePattern.test(qName)){
throw new Error('invalid attribute:'+qName)
}
this[this.length++] = {qName:qName,value:value,offset:offset}
},
length:0,
getLocalName:function(i){return this[i].localName},
getOffset:function(i){return this[i].offset},
getQName:function(i){return this[i].qName},
getURI:function(i){return this[i].uri},
getValue:function(i){return this[i].value}
// ,getIndex:function(uri, localName)){
// if(localName){
//
// }else{
// var qName = uri
// }
// },
// getValue:function(){return this.getValue(this.getIndex.apply(this,arguments))},
// getType:function(uri,localName){}
// getType:function(i){},
}
function _set_proto_(thiz,parent){
thiz.__proto__ = parent;
return thiz;
}
if(!(_set_proto_({},_set_proto_.prototype) instanceof _set_proto_)){
_set_proto_ = function(thiz,parent){
function p(){};
p.prototype = parent;
p = new p();
for(parent in thiz){
p[parent] = thiz[parent];
}
return p;
}
}
function split(source,start){
var match;
var buf = [];
var reg = /'[^']+'|"[^"]+"|[^\s<>\/=]+=?|(\/?\s*>|<)/g;
reg.lastIndex = start;
reg.exec(source);//skip <
while(match = reg.exec(source)){
buf.push(match);
if(match[1])return buf;
}
}
if(typeof require == 'function'){
exports.XMLReader = XMLReader;
}
},{}],5933:[function(require,module,exports){
(function (process,global){
/*
Copyright 2013 Yahoo! Inc. All rights reserved.
Licensed under the BSD License.
http://yuilibrary.com/license/
*/
/*jslint expr: true */
/*global define */
(function (global, factory) {
var built = factory();
/* istanbul ignore else */
if (typeof module === 'object' && module) {
module.exports = built;
}
/* istanbul ignore next */
if (typeof define === 'function' && define.amd) {
define(factory);
}
global.PromisePolyfill = built;
global.Promise || (global.Promise = built);
}(typeof global !== 'undefined' ? global : /* istanbul ignore next */ this, function () {
function isArray(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
}
function assign(obj, props) {
for (var prop in props) {
/* istanbul ignore else */
if (props.hasOwnProperty(prop)) {
obj[prop] = props[prop];
}
}
}
/**
A promise represents a value that may not yet be available. Promises allow
you to chain asynchronous operations, write synchronous looking code and
handle errors throughout the process.
This constructor takes a function as a parameter where you can insert the logic
that fulfills or rejects this promise. The fulfillment value and the rejection
reason can be any JavaScript value. It's encouraged that rejection reasons be
error objects
<pre><code>
var fulfilled = new Promise(function (resolve) {
resolve('I am a fulfilled promise');
});
var rejected = new Promise(function (resolve, reject) {
reject(new Error('I am a rejected promise'));
});
</code></pre>
@class Promise
@constructor
@param {Function} fn A function where to insert the logic that resolves this
promise. Receives `resolve` and `reject` functions as parameters.
This function is called synchronously.
**/
function Promise(fn) {
if (!(this instanceof Promise)) {
Promise._log('Promises should always be created with new Promise(). This will throw an error in the future', 'error');
return new Promise(fn);
}
var resolver = new Promise.Resolver(this);
/**
A reference to the resolver object that handles this promise
@property _resolver
@type Object
@private
*/
this._resolver = resolver;
try {
fn(function (value) {
resolver.resolve(value);
}, function (reason) {
resolver.reject(reason);
});
} catch (e) {
resolver.reject(e);
}
}
assign(Promise.prototype, {
/**
Schedule execution of a callback to either or both of "fulfill" and
"reject" resolutions for this promise. The callbacks are wrapped in a new
promise and that promise is returned. This allows operation chaining ala
`functionA().then(functionB).then(functionC)` where `functionA` returns
a promise, and `functionB` and `functionC` _may_ return promises.
Asynchronicity of the callbacks is guaranteed.
@method then
@param {Function} [callback] function to execute if the promise
resolves successfully
@param {Function} [errback] function to execute if the promise
resolves unsuccessfully
@return {Promise} A promise wrapping the resolution of either "resolve" or
"reject" callback
**/
then: function (callback, errback) {
// using this.constructor allows for customized promises to be
// returned instead of plain ones
var resolve, reject,
promise = new this.constructor(function (res, rej) {
resolve = res;
reject = rej;
});
this._resolver._addCallbacks(
typeof callback === 'function' ?
Promise._makeCallback(promise, resolve, reject, callback) : resolve,
typeof errback === 'function' ?
Promise._makeCallback(promise, resolve, reject, errback) : reject
);
return promise;
},
/*
A shorthand for `promise.then(undefined, callback)`.
Returns a new promise and the error callback gets the same treatment as in
`then`: errors get caught and turned into rejections, and the return value
of the callback becomes the fulfilled value of the returned promise.
@method catch
@param [Function] errback Callback to be called in case this promise is
rejected
@return {Promise} A new promise modified by the behavior of the error
callback
*/
'catch': function (errback) {
return this.then(undefined, errback);
}
});
/**
Wraps the callback in another function to catch exceptions and turn them
into rejections.
@method _makeCallback
@param {Promise} promise Promise that will be affected by this callback
@param {Function} fn Callback to wrap
@return {Function}
@static
@private
**/
Promise._makeCallback = function (promise, resolve, reject, fn) {
// callbacks and errbacks only get one argument
return function (valueOrReason) {
var result;
// Promises model exception handling through callbacks
// making both synchronous and asynchronous errors behave
// the same way
try {
// Use the argument coming in to the callback/errback from the
// resolution of the parent promise.
// The function must be called as a normal function, with no
// special value for |this|, as per Promises A+
result = fn(valueOrReason);
} catch (e) {
// calling return only to stop here
reject(e);
return;
}
if (result === promise) {
reject(new TypeError('Cannot resolve a promise with itself'));
return;
}
resolve(result);
};
};
/**
Logs a message. This method is designed to be overwritten with YUI's `log`
function.
@method _log
@param {String} msg Message to log
@param {String} type Log level. One of 'error', 'warn', 'info'.
@static
@private
**/
Promise._log = function (msg, type) { /* istanbul ignore else */ if (typeof console !== 'undefined') { console[type](msg); } };
/*
Ensures that a certain value is a promise. If it is not a promise, it wraps it
in one.
This method can be copied or inherited in subclasses. In that case it will
check that the value passed to it is an instance of the correct class.
This means that `PromiseSubclass.resolve()` will always return instances of
`PromiseSubclass`.
@method resolve
@param {Any} Any object that may or may not be a promise
@return {Promise}
@static
*/
Promise.resolve = function (value) {
if (value && value.constructor === this) {
return value;
}
/*jshint newcap: false */
return new this(function (resolve) {
/*jshint newcap: true */
resolve(value);
});
};
/*
A shorthand for creating a rejected promise.
@method reject
@param {Any} reason Reason for the rejection of this promise. Usually an Error
Object
@return {Promise} A rejected promise
@static
*/
Promise.reject = function (reason) {
/*jshint newcap: false */
var promise = new this(function () {});
/*jshint newcap: true */
// Do not go through resolver.reject() because an immediately rejected promise
// always has no callbacks which would trigger an unnecessary warnihg
promise._resolver._result = reason;
promise._resolver._status = 'rejected';
return promise;
};
/*
Returns a promise that is resolved or rejected when all values are resolved or
any is rejected. This is useful for waiting for the resolution of multiple
promises, such as reading multiple files in Node.js or making multiple XHR
requests in the browser.
@method all
@param {Any[]} values An array of any kind of values, promises or not. If a value is not
@return [Promise] A promise for an array of all the fulfillment values
@static
*/
Promise.all = function (values) {
var Promise = this;
return new Promise(function (resolve, reject) {
if (!isArray(values)) {
reject(new TypeError('Promise.all expects an array of values or promises'));
return;
}
var remaining = values.length,
i = 0,
length = values.length,
results = [];
function oneDone(index) {
return function (value) {
results[index] = value;
remaining--;
if (!remaining) {
resolve(results);
}
};
}
if (length < 1) {
return resolve(results);
}
for (; i < length; i++) {
Promise.resolve(values[i]).then(oneDone(i), reject);
}
});
};
/*
Returns a promise that is resolved or rejected when any of values is either
resolved or rejected. Can be used for providing early feedback in the UI
while other operations are still pending.
@method race
@param {Any[]} values An array of values or promises
@return {Promise}
@static
*/
Promise.race = function (values) {
var Promise = this;
return new Promise(function (resolve, reject) {
if (!isArray(values)) {
reject(new TypeError('Promise.race expects an array of values or promises'));
return;
}
// just go through the list and resolve and reject at the first change
// This abuses the fact that calling resolve/reject multiple times
// doesn't change the state of the returned promise
for (var i = 0, count = values.length; i < count; i++) {
Promise.resolve(values[i]).then(resolve, reject);
}
});
};
/**
Forces a function to be run asynchronously, but as fast as possible. In Node.js
this is achieved using `setImmediate` or `process.nextTick`. In YUI this is
replaced with `Y.soon`.
@method async
@param {Function} callback The function to call asynchronously
@static
**/
/* istanbul ignore next */
Promise.async = typeof setImmediate !== 'undefined' ?
function (fn) {setImmediate(fn);} :
typeof process !== 'undefined' && process.nextTick ?
process.nextTick :
function (fn) {setTimeout(fn, 0);};
/**
Represents an asynchronous operation. Provides a
standard API for subscribing to the moment that the operation completes either
successfully (`fulfill()`) or unsuccessfully (`reject()`).
@class Promise.Resolver
@constructor
@param {Promise} promise The promise instance this resolver will be handling
**/
function Resolver(promise) {
/**
List of success callbacks
@property _callbacks
@type Array
@private
**/
this._callbacks = [];
/**
List of failure callbacks
@property _errbacks
@type Array
@private
**/
this._errbacks = [];
/**
The promise for this Resolver.
@property promise
@type Promise
@deprecated
**/
this.promise = promise;
/**
The status of the operation. This property may take only one of the following
values: 'pending', 'fulfilled' or 'rejected'.
@property _status
@type String
@default 'pending'
@private
**/
this._status = 'pending';
/**
This value that this promise represents.
@property _result
@type Any
@private
**/
this._result = null;
}
assign(Resolver.prototype, {
/**
Resolves the promise, signaling successful completion of the
represented operation. All "onFulfilled" subscriptions are executed and passed
the value provided to this method. After calling `fulfill()`, `reject()` and
`notify()` are disabled.
@method fulfill
@param {Any} value Value to pass along to the "onFulfilled" subscribers
**/
fulfill: function (value) {
var status = this._status;
if (status === 'pending' || status === 'accepted') {
this._result = value;
this._status = 'fulfilled';
}
if (this._status === 'fulfilled') {
this._notify(this._callbacks, this._result);
// Reset the callback list so that future calls to fulfill()
// won't call the same callbacks again. Promises keep a list
// of callbacks, they're not the same as events. In practice,
// calls to fulfill() after the first one should not be made by
// the user but by then()
this._callbacks = [];
// Once a promise gets fulfilled it can't be rejected, so
// there is no point in keeping the list. Remove it to help
// garbage collection
this._errbacks = null;
}
},
/**
Resolves the promise, signaling *un*successful completion of the
represented operation. All "onRejected" subscriptions are executed with
the value provided to this method. After calling `reject()`, `resolve()`
and `notify()` are disabled.
@method reject
@param {Any} value Value to pass along to the "reject" subscribers
**/
reject: function (reason) {
var status = this._status;
if (status === 'pending' || status === 'accepted') {
this._result = reason;
this._status = 'rejected';
if (!this._errbacks.length) {Promise._log('Promise rejected but no error handlers were registered to it', 'info');}
}
if (this._status === 'rejected') {
this._notify(this._errbacks, this._result);
// See fulfill()
this._callbacks = null;
this._errbacks = [];
}
},
/*
Given a certain value A passed as a parameter, this method resolves the
promise to the value A.
If A is a promise, `resolve` will cause the resolver to adopt the state of A
and once A is resolved, it will resolve the resolver's promise as well.
This behavior "flattens" A by calling `then` recursively and essentially
disallows promises-for-promises.
This is the default algorithm used when using the function passed as the
first argument to the promise initialization function. This means that
the following code returns a promise for the value 'hello world':
var promise1 = new Promise(function (resolve) {
resolve('hello world');
});
var promise2 = new Promise(function (resolve) {
resolve(promise1);
});
promise2.then(function (value) {
assert(value === 'hello world'); // true
});
@method resolve
@param [Any] value A regular JS value or a promise
*/
resolve: function (value) {
if (this._status === 'pending') {
this._status = 'accepted';
this._value = value;
if ((this._callbacks && this._callbacks.length) ||
(this._errbacks && this._errbacks.length)) {
this._unwrap(this._value);
}
}
},
/**
If `value` is a promise or a thenable, it will be unwrapped by
recursively calling its `then` method. If not, the resolver will be
fulfilled with `value`.
This method is called when the promise's `then` method is called and
not in `resolve` to allow for lazy promises to be accepted and not
resolved immediately.
@method _unwrap
@param {Any} value A promise, thenable or regular value
@private
**/
_unwrap: function (value) {
var self = this, unwrapped = false, then;
if (!value || (typeof value !== 'object' &&
typeof value !== 'function')) {
self.fulfill(value);
return;
}
try {
then = value.then;
if (typeof then === 'function') {
then.call(value, function (value) {
if (!unwrapped) {
unwrapped = true;
self._unwrap(value);
}
}, function (reason) {
if (!unwrapped) {
unwrapped = true;
self.reject(reason);
}
});
} else {
self.fulfill(value);
}
} catch (e) {
if (!unwrapped) {
self.reject(e);
}
}
},
/**
Schedule execution of a callback to either or both of "resolve" and
"reject" resolutions of this resolver. If the resolver is not pending,
the correct callback gets called automatically.
@method _addCallbacks
@param {Function} [callback] function to execute if the Resolver
resolves successfully
@param {Function} [errback] function to execute if the Resolver
resolves unsuccessfully
**/
_addCallbacks: function (callback, errback) {
var callbackList = this._callbacks,
errbackList = this._errbacks;
// Because the callback and errback are represented by a Resolver, it
// must be fulfilled or rejected to propagate through the then() chain.
// The same logic applies to resolve() and reject() for fulfillment.
if (callbackList) {
callbackList.push(callback);
}
if (errbackList) {
errbackList.push(errback);
}
switch (this._status) {
case 'accepted':
this._unwrap(this._value);
break;
case 'fulfilled':
this.fulfill(this._result);
break;
case 'rejected':
this.reject(this._result);
break;
}
},
/**
Executes an array of callbacks from a specified context, passing a set of
arguments.
@method _notify
@param {Function[]} subs The array of subscriber callbacks
@param {Any} result Value to pass the callbacks
@protected
**/
_notify: function (subs, result) {
// Since callback lists are reset synchronously, the subs list never
// changes after _notify() receives it. Avoid calling Y.soon() for
// an empty list
if (subs.length) {
// Calling all callbacks after Promise.async to guarantee
// asynchronicity. Because setTimeout can cause unnecessary
// delays that *can* become noticeable in some situations
// (especially in Node.js)
Promise.async(function () {
var i, len;
for (i = 0, len = subs.length; i < len; ++i) {
subs[i](result);
}
});
}
}
});
Promise.Resolver = Resolver;
return Promise;
}));
}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"_process":5966}],5934:[function(require,module,exports){
var css = "/* ======================================================================= */\n/* ======================================================================= */\n/* ======================================================================= */\n/* Definition of itag shadow-css is done by defining a `dummy` css-rule */\n/* for the dummy-element: `itag-css` --> its property (also dummy) `i-tag` /*\n/* will define which itag will be css-shadowed /*\n/* ======================================================================= */\nitag-css {\n i-tag: i-button; /* set the property-value to the proper itag */\n}\n/* ======================================================================= */\n/* ======================================================================= */\n/* ======================================================================= */\n\n\n/* ================================= */\n/* set invisiblity when not rendered */\n/* ================================= */\ni-button:not(.itag-rendered) {\n /* don't set visibility to hidden --> you cannot set a focus on those items */\n opacity: 0 !important;\n position: absolute !important;\n left: -9999px !important;\n top: -9999px !important;\n z-index: -1;\n}\n\ni-button:not(.itag-rendered) * {\n opacity: 0 !important;\n}\n/* ================================= */\n\ni-button {\n margin: 0;\n display: inline-block;\n *display: inline; /*IE 6/7*/\n zoom: 1;\n position: relative;\n vertical-align: middle;\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n\ni-button.i-primary >button,\ni-button.i-focussed >button {\n background-color: rgb(0, 120, 231);\n color: #fff;\n}\n\n/*csslint outline-none:false*/\n\ni-button >button {\n -webkit-touch-callout: none;\n -webkit-user-select: none;\n -khtml-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n position: relative;\n padding: 0.5em 1em;\n color: inherit; /* 1 */\n font: inherit; /* 2 */\n margin: 0; /* 3 */\n overflow: visible;\n text-transform: none;\n -webkit-appearance: button; /* 2 */\n line-height: normal;\n white-space: nowrap;\n vertical-align: baseline;\n text-align: center;\n cursor: pointer;\n -webkit-user-drag: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n font-family: inherit;\n font-size: 100%;\n color: #444; /* rgba not supported (IE 8) */\n color: rgba(0, 0, 0, 0.80); /* rgba supported */\n border: 1px solid #999; /*IE 6/7/8*/\n border: none rgba(0, 0, 0, 0); /*IE9 + everything else*/\n background-color: #E6E6E6;\n text-decoration: none;\n border-radius: 2px;\n box-shadow: 0 0 0 1px rgba(0,0,0, 0.15) inset;\n}\n\n\ni-button.i-hover >button,\ni-button >button:hover,\ni-button >button:focus {\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#1a000000',GradientType=0);\n background-image: -webkit-gradient(linear, 0 0, 0 100%, from(transparent), color-stop(40%, rgba(0,0,0, 0.05)), to(rgba(0,0,0, 0.10)));\n background-image: -webkit-linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10));\n background-image: -moz-linear-gradient(top, rgba(0,0,0, 0.05) 0%, rgba(0,0,0, 0.10));\n background-image: -o-linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10));\n background-image: linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10));\n}\n\ni-button >button:focus {\n box-shadow: 0 0 0 1px rgba(0,0,0, 0.6) inset;\n}\n\ni-button.i-hover >button,\ni-button >button:hover,\ni-button.i-hover >button:focus,\ni-button >button:hover:focus {\n box-shadow: 0 0 0 1px rgba(0,0,0, 0.7) inset;\n}\n\ni-button >button:focus {\n outline: 0;\n}\n\ni-button.i-active.i-hover >button,\ni-button.i-active >button:hover,\ni-button.i-active.i-hover >button:focus,\ni-button.i-active >button:hover:focus,\ni-button.i-hover >button.pure-button-active, /* need .pure-button-active for it gets set when \"enter-pressed\" on button */\ni-button >button.pure-button-active:hover, /* need .pure-button-active for it gets set when \"enter-pressed\" on button */\ni-button.i-hover >button.pure-button-active:focus, /* need .pure-button-active for it gets set when \"enter-pressed\" on button */\ni-button >button.pure-button-active:hover:focus, /* need .pure-button-active for it gets set when \"enter-pressed\" on button */\ni-button >button.pure-button-active, /* need .pure-button-active for it gets set when \"enter-pressed\" on button */\ni-button >button.pure-button-active:focus, /* need .pure-button-active for it gets set when \"enter-pressed\" on button */\ni-button.i-hover >button:active,\ni-button >button:hover:active,\ni-button.i-hover >button:focus:active,\ni-button >button:hover:focus:active {\n box-shadow: 0 0 0 1px rgba(0,0,0, 0.8) inset, 0 0 6px rgba(0,0,0, 0.4) inset;\n}\n\ni-button.i-disabled >button,\ni-button[disabled=\"true\"] >button,\ni-button.i-disabled >button:active,\ni-button[disabled=\"true\"] >button:active,\ni-button.i-disabled >button.pure-button-active, /* need .pure-button-active for it gets set when \"enter-pressed\" on button */\ni-button[disabled=\"true\"] >button.pure-button-active,\ni-button.i-disabled.i-active >button,\ni-button.i-active[disabled=\"true\"] >button,\ni-button.i-disabled >button:focus,\ni-button[disabled=\"true\"] >button:focus,\ni-button.i-disabled.focussed >button,\ni-button.focussed[disabled=\"true\"] >button,\ni-button.i-disabled >button:hover,\ni-button[disabled=\"true\"] >button:hover {\n border: none;\n background-image: none;\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n filter: alpha(opacity=60);\n -khtml-opacity: 0.60;\n -moz-opacity: 0.60;\n opacity: 0.60;\n cursor: not-allowed;\n box-shadow: 0 0 0 1px rgba(0,0,0, 0.15) inset;\n cursor: default;\n}\n\ni-button.i-rounded >button {\n border-radius: 0.3em;\n}\n\ni-button.i-heavyrounded >button {\n border-radius: 0.5em;\n}\n\ni-button.i-oval >button {\n border-radius: 50%;\n}\n\ni-button.i-halfoval >button {\n border-radius: 25%;\n}"; (require("/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify"))(css); module.exports = css;
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],5935:[function(require,module,exports){
module.exports = function (window) {
"use strict";
require('itags.core')(window);
require('./css/i-button.css');
var itagName = 'i-button', // <-- define your own itag-name here
ITSA = window.ITSA,
Event = ITSA.Event,
Itag, IFormElement;
if (!window.ITAGS[itagName]) {
IFormElement = require('i-formelement')(window);
Event.before(itagName+':manualfocus', function(e) {
// the i-select itself is unfocussable, but its button is
// we need to patch `manualfocus`,
// which is emitted on node.focus()
// a focus by userinteraction will always appear on the button itself
// so we don't bother that
var element = e.target;
e.preventDefault();
element.itagReady().then(
function() {
var button = element.getElement('button');
button && button.focus(true);
}
);
});
Event.after('tap', function(e) {
var element = e.target.getParent(),
model = element.model;
if (!model.disabled) {
/**
* Emitted when a the i-select changes its value
*
* @event i-button:tap
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the i-select element
* @param e.buttonType {String} the type of the button, equals model.type
* @since 0.1
*/
element.emit('tap', {
buttonType: model.type
});
}
}, 'i-button > button');
Itag = IFormElement.subClass(itagName, {
/*
*
* @property attrs
* @type Object
* @since 0.0.1
*/
attrs: {
disabled: 'boolean',
type: 'string'
},
init: function() {
var element = this,
designNode = element.getItagContainer(),
buttonText = designNode.getHTML();
// when initializing: make sure NOT to overrule model-properties that already
// might have been defined when modeldata was boundend. Therefore, use `defineWhenUndefined`
element.defineWhenUndefined('text', (buttonText==='') ? ' ' : buttonText); // sets element.model.someprop = somevalue; when not defined yet
},
render: function() {
this.setHTML('<button></button>');
},
sync: function() {
var element = this,
button = element.getElement('button');
button.setHTML(element.model.text);
},
destroy: function() {
}
});
window.ITAGS[itagName] = Itag;
}
return window.ITAGS[itagName];
};
},{"./css/i-button.css":5934,"i-formelement":5940,"itags.core":5953}],5936:[function(require,module,exports){
var css = "/* ======================================================================= */\n/* ======================================================================= */\n/* ======================================================================= */\n/* Definition of itag shadow-css is done by defining a `dummy` css-rule */\n/* for the dummy-element: `itag-css` --> its property (also dummy) `i-tag` /*\n/* will define which itag will be css-shadowed /*\n/* ======================================================================= */\nitag-css {\n i-tag: i-checkbox; /* set the property-value to the proper itag */\n}\n/* ======================================================================= */\n/* ======================================================================= */\n/* ======================================================================= */\n\n\n/* ================================= */\n/* set invisiblity when not rendered */\n/* ================================= */\ni-checkbox:not(.itag-rendered) {\n /* don't set visibility to hidden --> you cannot set a focus on those items */\n opacity: 0 !important;\n position: absolute !important;\n left: -9999px !important;\n top: -9999px !important;\n z-index: -1;\n}\n\ni-checkbox:not(.itag-rendered) * {\n opacity: 0 !important;\n}\n/* ================================= */\ni-checkbox {\n font-size: 0.9em;\n margin: 0;\n padding: 0;\n display: inline-block;\n position: relative;\n vertical-align: middle;\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n width: 4.5em;\n height: 1.9em;\n}\n\ni-checkbox span {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n\ni-checkbox >span,\ni-checkbox >span >span,\ni-checkbox >span >span >span,\ni-checkbox >span >span >span >span {\n display: block;\n}\n\n/* the first span is the focussable span */\ni-checkbox >span {\n z-index: 1;\n overflow: hidden;\n border: 1px solid #CCC;\n width: 100%;\n height: 100%;\n}\n\ni-checkbox >span:focus .i-on,\ni-checkbox >span:focus .i-off {\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#1a000000',GradientType=0);\n background-image: -webkit-gradient(linear, 0 0, 0 100%, from(transparent), color-stop(40%, rgba(0,0,0, 0.05)), to(rgba(0,0,0, 0.10)));\n background-image: -webkit-linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10));\n background-image: -moz-linear-gradient(top, rgba(0,0,0, 0.05) 0%, rgba(0,0,0, 0.10));\n background-image: -o-linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10));\n background-image: linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10));\n box-shadow: 0 0 0 1px rgba(0,0,0, 0.3) inset;\n}\n\ni-checkbox >span:focus .i-btn {\n border: solid 1px #888;\n box-shadow: inset 0 0.17em 0.5em rgba(0, 0, 0, 0.06);\n}\n\ni-checkbox >span:focus .i-btn.dd-dragging {\n cursor: default;\n}\n\ni-checkbox .i-constrain {\n height: 100%;\n padding: 0;\n margin: 0;\n position: relative;\n top: 0; /* left will be calculated by i-checkbox */\n}\n\ni-checkbox .i-container {\n position: relative;\n cursor: pointer;\n padding: 0;\n margin: 0;\n height: 100%;\n left: 0;\n -webkit-touch-callout: none;\n -webkit-user-select: none;\n -khtml-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n -webkit-transition: left 0.25s;\n -moz-transition: left 0.25s;\n -ms-transition: left 0.25s;\n -o-transition: left 0.25s;\n transition: left 0.25s;\n}\n\ni-checkbox .i-on {\n height: 100%;\n background-color: #0078E7;\n color: #FFF;\n position: static;\n text-align: center;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n padding-top: 2px;\n float: left;\n box-shadow: inset 0 0.3em 0.5em rgba(0, 0, 0, 0.18);\n}\n\ni-checkbox .i-off {\n height: 100%;\n background-color: #FFF;\n color: #444;\n position: static;\n text-align: center;\n padding-top: 2px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n float: left;\n box-shadow: inset 0 0.3em 0.5em rgba(0, 0, 0, 0.18);\n}\n\ni-checkbox .i-btn {\n position: relative;\n z-index: 1;\n border-radius: 100%;\n float: left;\n box-shadow: inset 0 0.17em 0.5em rgba(0, 0, 0, 0.18);\n background-color: #fff;\n border: solid 1px #AAA;\n}"; (require("/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify"))(css); module.exports = css;
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],5937:[function(require,module,exports){
module.exports = function (window) {
"use strict";
require('./css/i-checkbox.css');
require('itags.core')(window);
var itagName = 'i-checkbox', // <-- define your own itag-name here
ITSA = window.ITSA,
Event = ITSA.Event,
later = ITSA.later,
DEFAULT_ON_TEXT = 'I',
DEFAULT_OFF_TEXT = 'O',
SUPPRESS_DELAY = 50,
INTERVAL_FONTCHANGE_CHECK = 350,
Itag, IFormElement, registeredElements, timer, registerElement, unRegisterElement;
if (!window.ITAGS[itagName]) {
ITSA.DD.init();
registeredElements = [];
IFormElement = require('i-formelement')(window);
Event.before(itagName+':manualfocus', function(e) {
// the i-select itself is unfocussable, but its button is
// we need to patch `manualfocus`,
// which is emitted on node.focus()
// a focus by userinteraction will always appear on the button itself
// so we don't bother that
var element = e.target;
e.preventDefault();
element.itagReady().then(
function() {
var focusNode = element.getElement('>span');
focusNode && focusNode.focus(true, true);
}
);
});
Event.after('tap', function(e) {
var element = e.target,
model = element.model,
focusNode;
if (!element.hasData('_suppressTap')) {
model.checked = !model.checked;
focusNode = element.getElement('>span');
focusNode.hasFocus() || focusNode.focus();
}
}, 'i-checkbox');
Event.after('keypress', function(e) {
var element = e.target,
model = element.model;
(e.charCode===32) && (model.checked=!model.checked);
}, 'i-checkbox');
Event.after('dd-drag', function(e) {
var checkbox = e.target.inside('i-checkbox');
checkbox.setData('_suppressTap', true);
}, 'i-checkbox');
Event.after('dd-drop', function(e) {
var dragNode = e.target,
checkbox = dragNode.inside('i-checkbox'),
btnNode = dragNode.getElement('>.i-btn'),
distance = btnNode.left - checkbox.left - checkbox.getData('_leftBorder') + Math.round(checkbox.getData('_height')/2);
checkbox.model.checked = (distance>Math.round(checkbox.getData('_width')/2));
checkbox._setUIState();
later(function() {
checkbox.removeData('_suppressTap');
}, SUPPRESS_DELAY);
}, 'i-checkbox');
Event.defineEvent(itagName+':checkedchange').unPreventable();
Event.after(itagName+':change', function(e) {
var element = e.target,
model = element.model;
/**
* Emitted when a the i-checkbox changes its `checked`-value
*
* @event i-checkbox:checkedchange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the i-checkbox element
* @param e.prevValue {Boolean}
* @param e.newValue {Boolean}
* @since 0.1
*/
element.emit('checkedchange', {
prevValue: !model.checked,
newValue: model.checked
});
});
// whenever fontsize changes, the calculated height needs to change
// we have no means of listening to this - even it's unlikely to happen
// yet we want the i-checkboxes to fit at anytime, so we set up a lazy timer
// that checks all registered itag-instances
registerElement = function(element) {
registeredElements.push(element);
if (!timer) {
timer = later(function() {
var len = registeredElements.length,
i, element;
for (i=0; i<len; i++) {
element = registeredElements[i];
if (!element.hasData('_suppressTap')) {
element._fitCheckbox();
element._setUIState();
}
}
}, INTERVAL_FONTCHANGE_CHECK, true);
}
};
unRegisterElement = function(element) {
registeredElements.remove(element);
if ((registeredElements.length===0) && timer) {
timer.cancel();
timer = null;
}
};
Itag = IFormElement.subClass(itagName, {
attrs: {
checked: 'boolean',
'reset-value': 'boolean'
},
init: function() {
var element = this,
value = element.model.checked,
designNode = element.getItagContainer(),
options = designNode.getAll('>option');
// set the reset-value to the inital-value in case `reset-value` was not present
element.defineWhenUndefined('reset-value', value)
.defineWhenUndefined('on-text', options[0] ? options[0].getHTML() : DEFAULT_ON_TEXT)
.defineWhenUndefined('off-text', options[1] ? options[1].getHTML() : DEFAULT_OFF_TEXT);
registerElement(element);
},
render: function() {
var element = this,
content, innerDiv, borderLeftWidth;
content = '<span tabindex="0">'+
'<span class="i-constrain">'+
'<span class="i-container" dd-draggable="true" dd-handle=".i-btn" constrain-selector=".i-constrain">'+
'<span class="i-on">I</span>'+
'<span class="i-off">O</span>'+
'<span class="i-btn"></span>'+
'</span>'+
'</span>'+
'</span>';
element.setHTML(content);
innerDiv = element.getElement('>span');
borderLeftWidth = parseInt(innerDiv.getStyle('border-left-width'), 10);
element.setData('_leftBorder', borderLeftWidth);
element.setData('_vertBorders', parseInt(innerDiv.getStyle('border-top-width'), 10) + parseInt(innerDiv.getStyle('border-bottom-width'), 10));
element.setData('_horBorders', borderLeftWidth + parseInt(innerDiv.getStyle('border-right-width'), 10));
},
_fitCheckbox: function() {
var element = this,
// width = parseInt(element.getStyle('width'), 10),
// height = parseInt(element.getStyle('height'), 10),
width = element.offsetWidth,
height = element.offsetHeight,
innerDiv, constrainNode, innerNodes, halfHeight, shift;
if ((width!==element.getData('_width')) || (height!==element.getData('_height'))) {
// the height that should be used by the innernodes, schould be decreased by the border-width
height -= element.getData('_vertBorders');
width -= element.getData('_horBorders');
innerDiv = element.getElement('>span');
constrainNode = innerDiv.getElement('>span');
innerNodes = constrainNode.getAll('>span >span');
halfHeight = Math.round(height/2);
shift = 3*Math.round(height/4);
innerDiv.setInlineStyle('border-radius', height+'px');
constrainNode.setInlineStyles([
{property: 'left', value: (height-width)+'px'},
{property: 'width', value: (3*width-height)+'px'}
]);
innerNodes[0].setInlineStyles([
{property: 'border-radius', value: height+'px'},
{property: 'line-height', value: (height-2)+'px'}, // correct with 2px: the padding-top of i-on
{property: 'padding-right', value: shift+'px'},
{property: 'width', value: width+'px'}
]);
innerNodes[1].setInlineStyles([
{property: 'border-radius', value: height+'px'},
{property: 'margin-left', value: -height+'px'},
{property: 'line-height', value: (height-2)+'px'}, // correct with 2px: the padding-top of i-off
{property: 'padding-left', value: shift+'px'},
{property: 'width', value: width+'px'}
]);
innerNodes[2].setInlineStyles([
{property: 'left', value: -width+'px'},
{property: 'height', value: height+'px'},
{property: 'width', value: height+'px'}
]);
element.setData('_width', width);
element.setData('_height', height);
}
},
_setUIState: function() {
var element = this,
container = element.getElement('>span >span >span'),
newValue = element.model.checked ? (element.getData('_width')-element.getData('_height'))+'px' : '0';
(container.getInlineStyle('left')===newValue) || container.setInlineStyle('left', newValue);
},
sync: function() {
var element = this,
container = element.getElement('>span >span >span'),
itemContainers = container.getAll('>span'),
model = element.model;
element._fitCheckbox();
element._setUIState(model.checked);
itemContainers[0].setHTML(model['on-text']);
itemContainers[1].setHTML(model['off-text']);
},
currentToReset: function() {
var model = this.model;
model['reset-value'] = model.checked;
},
reset: function() {
var model = this.model;
model.checked = model['reset-value'];
},
destroy: function() {
unRegisterElement(this);
}
});
window.ITAGS[itagName] = Itag;
}
return window.ITAGS[itagName];
};
},{"./css/i-checkbox.css":5936,"i-formelement":5940,"itags.core":5953}],5938:[function(require,module,exports){
var css = "/* ======================================================================= */\n/* ======================================================================= */\n/* ======================================================================= */\n/* Definition of itag shadow-css is done by defining a `dummy` css-rule */\n/* for the dummy-element: `itag-css` --> its property (also dummy) `i-tag` /*\n/* will define which itag will be css-shadowed /*\n/* ======================================================================= */\nitag-css {\n i-tag: i-select; /* set the property-value to the proper itag */\n}\n/* ======================================================================= */\n/* ======================================================================= */\n/* ======================================================================= */\n\n\n/* =================================== */\n/* set invisiblity when not rendered */\n/* =================================== */\ni-form.hide-children,\ni-form:not(.itag-rendered) {\n /* don't set visibility to hidden --> you cannot set a focus on those items */\n opacity: 0 !important;\n position: absolute !important;\n left: -9999px !important;\n top: -9999px !important;\n z-index: -1;\n}\n/* ================================= */\n\n/*!\n * Most styles are from Pure v0.5.0\n * Licensed under the BSD License.\n*/\n\ni-form {\n margin: 0;\n padding: 0.25em 0.5em;\n display: block;\n}\n\ni-form[active-labels=\"true\"] i-label {\n cursor: default;\n}\n\ni-form fieldset {\n margin: 0 0 0.8em;\n padding: 0.75em 0;\n border: 1px solid #C0C0C0;\n border-radius: 2px;\n background-color: rgba(0, 0, 0, 0.03);\n width: 100%;\n display: block;\n}\n\ni-form fieldset i-button {\n margin-top: 0.75em;\n margin-right: 0.25em;\n}\n\ni-form legend {\n display: block;\n width: 100%;\n padding: 0.3em 0;\n margin-bottom: 1em;\n color: #333;\n border-bottom: 1px solid #e5e5e5;\n}\n\ni-form div.i-formrow {\n margin: 0;\n padding: 0;\n display: inline-block;\n}\n\n/* ======================================================== */\n/* Grouped Inputs */\ni-form.i-group:not(.i-aligned) i-label,\ni-form:not(.i-aligned) .i-group i-label {\n opacity: 0 !important;\n position: absolute !important;\n left: -9999px !important;\n top: -9999px !important;\n z-index: -1;\n}\ni-form.i-group [itag-formelement=\"true\"]:not(i-button),\ni-form .i-group [itag-formelement=\"true\"]:not(i-button) {\n display: block;\n}\ni-form.i-group i-input input,\ni-form .i-group i-input input {\n border-radius: 0;\n position: relative;\n top: -1px;\n}\ni-form.i-group div.i-formrow:first-child i-input input,\ni-form .i-group div.i-formrow:first-child i-input input {\n top: 1px;\n border-radius: 4px 4px 0 0;\n}\ni-form.i-group div.i-formrow:last-child i-input input,\ni-form .i-group div.i-formrow:last-child i-input input {\n top: -2px;\n border-radius: 0 0 4px 4px;\n}\ni-form.i-group div.i-formrow:first-child i-label,\ni-form .i-group div.i-formrow:first-child i-label {\n margin-top: 1px;\n}\ni-form.i-group div.i-formrow:last-child i-label,\ni-form .i-group div.i-formrow:last-child i-label {\n margin-top: -1px; /* not -2px: that would disturb appearance too much */;\n}\ni-form.i-group i-input input:focus,\ni-form .i-group i-input input:focus {\n z-index: 2;\n}\ni-form.i-group i-button,\ni-form.i-group i-select,\ni-form .i-group i-button,\ni-form .i-group i-select {\n margin: 0.35em 0;\n}\n/* ======================================================== */\n\n\n\n/* ======================================================== */\n/* Stacked */\ni-form.i-stacked div.i-formrow {\n display: block;\n}\ni-form.i-stacked div.i-formrow.itag-noblock i-label {\n display: inline-block;\n margin-right: 1em;\n}\ni-form.i-stacked [itag-formelement=\"true\"]:not(i-button) {\n display: block;\n margin: 0.25em 0;\n}\ni-form.i-stacked div.i-formrow.itag-noblock [itag-formelement=\"true\"]:not(i-button) {\n display: inline-block;\n margin: 0;\n}\ni-form.i-stacked i-label {\n display: block;\n margin: 1em 0 0.4em 0.15em;\n}\ni-form.i-stacked fieldset div.i-formrow:first-child i-label {\n margin-top: 0;\n}\ni-form.i-stacked .i-group i-input,\ni-form.i-aligned .i-group i-input,\ni-form .i-stacked .i-group i-input,\ni-form .i-aligned .i-group i-input {\n margin: 0;\n}\n/* ======================================================== */\n\n\n\n/* ======================================================== */\n/* Aligned */\ni-form.i-aligned div.i-formrow {\n display: block;\n}\ni-form.i-aligned i-label,\ni-form.i-aligned i-button,\ni-form.i-aligned [itag-formelement=\"true\"]:not(i-button) {\n display: inline-block;\n *display: inline;\n *zoom: 1;\n vertical-align: middle;\n}\ni-form.i-aligned div.i-formrow {\n margin-bottom: 1em;\n}\ni-form.i-aligned .i-group div.i-formrow,\ni-form .i-aligned .i-group div.i-formrow {\n margin-bottom: 0;\n}\ni-form.i-aligned div.i-formrow:last-child {\n margin-bottom: 0;\n}\ni-form.i-aligned i-label {\n text-align: right;\n display: inline-block;\n vertical-align: middle;\n width: 10em;\n margin: 0 1em 0 0;\n}\ni-form.i-aligned i-textarea {\n vertical-align: top;\n}\ni-form.i-aligned.i-group,\ni-form.i-aligned .i-group {\n margin-bottom: 0.8em;\n}\ni-form.i-aligned.i-group i-label label,\ni-form.i-aligned .i-group i-label label {\n text-align: right;\n}\ni-form.i-aligned fieldset i-button:first-child {\n margin-left: 11em;\n}\n/* ======================================================== */\n\n\n\n/* ======================================================== */\n/* Inline help for forms */\ni-form .message-inline {\n display: inline-block;\n padding-left: 0.3em;\n color: #666;\n vertical-align: middle;\n font-size: 0.875em;\n}\n\n/* Block help for forms */\ni-form .message {\n display: block;\n color: #666;\n font-size: 0.875em;\n}\n/* ======================================================== */\n\n\n\n@media only screen and (max-width : 480px) {\n i-form {\n display: block;\n width: 100%;\n }\n\n i-form i-label,\n i-form.i-aligned i-label {\n display: block;\n text-align: left;\n margin: 1em 0 0.4em 3px; /* 3px as lect-padding --> needs to fit .itag-noblock labels */\n }\n\n i-form fieldset div.i-formrow:first-child i-label,\n i-form.i-aligned fieldset div.i-formrow:first-child i-label {\n margin-top: 0;\n }\n\n i-form.i-aligned div.i-formrow.itag-noblock i-label {\n display: inline-block;\n width: 100%;\n padding-left: 85px; /* 82 + 3 extra pixels */\n margin-left: -82px;\n margin-bottom: 0;\n }\n\n i-form.i-aligned div.i-formrow.itag-noblock i-checkbox {\n margin-right: 12px;\n width: 70px !important;\n }\n\n i-form.i-aligned div.i-formrow.itag-noblock {\n text-align: right;\n }\n\n i-form.i-group i-label,\n i-form .i-group i-label {\n opacity: 0 !important;\n position: absolute !important;\n left: -9999px !important;\n top: -9999px !important;\n z-index: -1;\n }\n\n i-form i-input {\n display: block;\n text-align: left;\n margin-bottom: 0.3em;\n }\n\n i-form.i-group i-input,\n i-form .i-group i-input {\n margin-bottom: 0em;\n }\n\n i-form.i-aligned .control {\n margin: 1.5em 0 0 0;\n }\n\n i-form i-button,\n i-form i-select {\n display: block;\n width: 100%;\n }\n\n i-form fieldset i-button {\n margin-right: 0;\n margin-top: 0;\n }\n\n i-form.i-aligned fieldset i-button:first-child {\n margin-left: 0;\n }\n\n i-form i-button button,\n i-form i-select >button,\n i-form i-select >div >div {\n width: 100%;\n }\n\n i-form i-select >button {\n max-width: inherit;\n }\n\n i-form i-select > button div.btntext {\n margin: 0;\n max-width: inherit;\n width: 100%;\n text-align: center;\n }\n\n i-form i-select li {\n text-align: center;\n }\n\n i-form i-button:last-child {\n margin: 1em 0 0;\n }\n\n i-form .message-inline,\n i-form .message {\n display: block;\n font-size: 0.75em;\n /* Increased bottom padding to make it group with its related input element. */\n padding: 0.2em 0 0.8em;\n }\n}"; (require("/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify"))(css); module.exports = css;
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],5939:[function(require,module,exports){
module.exports = function (window) {
"use strict";
require('./css/i-form.css');
var NAME = '[i-form]: ',
itagName = 'i-form', // <-- define your own itag-name here
itagCore = require('itags.core')(window),
DOCUMENT = window.document,
ITSA = window.ITSA,
Event = ITSA.Event,
DEFAULT_KEYUP = 'shift+9',
DEFAULT_KEYDOWN = '9',
DEFAULT_SELECTOR = '[itag-formelement]',
DEFAULT_LOOP = true,
Itag;
if (!window.ITAGS[itagName]) {
Event.before(itagName+':manualfocus', function(e) {
// the i-select itself is unfocussable, but its button is
// we need to patch `manualfocus`,
// which is emitted on node.focus()
// a focus by userinteraction will always appear on the button itself
// so we don't bother that
var element = e.target;
e.preventDefault();
element.itagReady().then(
function() {
var selector = element.getFocusManagerSelector(),
focusNode = element.getElement(selector);
focusNode && focusNode.focus();
}
);
});
Event.after('i-button:tap', function(e) {
var ibutton = e.target,
iform = ibutton.inside('i-form');
if (iform && !iform.model.disabled) {
iform.emitAction({
button: ibutton,
buttonType: e.buttonType
});
}
}, 'i-form');
Event.defineEvent('i-form:reset')
.defaultFn(function(e) {
e.target.reset();
});
Event.after('i-button#reset:tap', function(e) {
var ibutton = e.target,
iform = ibutton.inside('i-form');
if (iform && !iform.model.disabled) {
/**
* Emitted when a the i-select changes its value
*
* @event i-form:reset
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the i-form element
* @param e.button {HTMLElement} the i-button#reset that caused the reset
* @since 0.1
*/
iform.emit('reset', {
button: ibutton
});
}
}, 'i-form');
Itag = DOCUMENT.createItag(itagName, {
attrs: {
'active-labels': 'boolean', // to give labels functionality of focussing on itags
disabled: 'boolean'
},
init: function() {
var element = this;
// now activate the focusmanager:
if (!element.isPlugged('fm')) {
element.plug('fm', {
'keyup': String(element.defFmKeyup()),
'keydown': String(element.defFmKeydown()),
'noloop': String(!element.defFmLoop()),
'manage': String(element.getFocusManagerSelector())
});
}
element.databinders = [];
},
render: function() {
var element = this,
designNode = element.getItagContainer(),
allFormElements, children;
// we must add a classname to the i-form and remove it when all
// i-form-elements are ready. This we need to prevent the i-form-elements
// to show some initial value before they are bounded
// now we add all i-form-elements that need to wait for bounded data to a hash
// fully set the designNode's content into the i-form:
element.setHTML(designNode.getHTML(null, true));
allFormElements = element.getAll('[i-prop], i-label');
if (allFormElements.length>0) {
element.setClass('hide-children');
children = [];
allFormElements.forEach(function(formElement) {
// first tell the element it needs to wait for data:
formElement.hasAttribute('i-prop') && formElement.setAttr('bound-model', 'true');
// now add the readypromise to the hash:
formElement._showItagPromise = window.Promise.manage();
children[children.length] = formElement._showItagPromise;
formElement.itagReady().then(formElement._showItagPromise.fulfill());
});
window.Promise.finishAll(children).then(
function() {
element.removeClass('hide-children');
}
);
}
ITSA.async(function() {
element.bind();
});
},
getFocusManagerSelector: function() {
return DEFAULT_SELECTOR;
},
defFmSelector: function() {
return DEFAULT_SELECTOR;
},
defFmKeyup: function() {
return DEFAULT_KEYUP;
},
defFmKeydown: function() {
return DEFAULT_KEYDOWN;
},
defFmLoop: function() {
return DEFAULT_LOOP;
},
emitAction: function(payload) {
/**
* Emitted when a the i-select changes its value
*
* @event i-form:action
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the i-form element
* @param e.button {HtmlElement} the i-button that was pressed
* @param e.buttonType {String}
* @since 0.1
*/
this.emit('action', payload);
},
_afterBindModel: function() {
this.unbind();
this.bind();
},
bind: function() {
var element = this,
databinders = element.databinders,
model = element.model,
allFormElements, propertyModel;
allFormElements = element.getAll('[i-prop]');
allFormElements.forEach(function(formElement) {
var property = formElement.getAttr('i-prop');
if (property) {
propertyModel = model[property];
if (propertyModel) {
databinders[databinders.length] = formElement.bindModel(propertyModel, true);
}
else {
// fulfill the promise from the hash, to make the hash completely fulfilled and the i-form to show:
formElement._showItagPromise && formElement._showItagPromise.fulfill();
console.warn(NAME+'Form-element waits for prop: '+property+', but this property is not bound. Will show the form, but not this element.');
}
}
});
},
currentToReset: function() {
// will set the current value as the reset-value: for all form elements
this.getAll('[itag-formelement]').forEach(function(element) {
element.currentToReset && element.currentToReset();
});
},
reset: function() {
// will reset all form elements
this.getAll('[itag-formelement]').forEach(function(element) {
element.reset && element.reset();
});
},
unbind: function() {
var element = this;
element.databinders.forEach(function(databinder) {
databinder.detach();
});
},
destroy: function() {
this.unbind();
}
}, false); // not subclassable, for we mostly need the css which should be retained
itagCore.setContentVisibility(Itag, true);
window.ITAGS[itagName] = Itag;
}
return window.ITAGS[itagName];
};
},{"./css/i-form.css":5938,"itags.core":5953}],5940:[function(require,module,exports){
module.exports = function (window) {
"use strict";
require('itags.core')(window);
var itagName = 'i-formelement', // <-- define your own itag-name here
DOCUMENT = window.document,
Itag;
if (!window.ITAGS[itagName]) {
Itag = DOCUMENT.createItag(itagName, {
init: function() {
this.setAttr('itag-formelement', 'true', true);
}
});
window.ITAGS[itagName] = Itag;
}
return window.ITAGS[itagName];
};
},{"itags.core":5953}],5941:[function(require,module,exports){
var css = "/* ======================================================================= */\n/* ======================================================================= */\n/* ======================================================================= */\n/* Definition of itag shadow-css is done by defining a `dummy` css-rule */\n/* for the dummy-element: `itag-css` --> its property (also dummy) `i-tag` /*\n/* will define which itag will be css-shadowed /*\n/* ======================================================================= */\nitag-css {\n i-tag: i-input; /* set the property-value to the proper itag */\n}\n/* ======================================================================= */\n/* ======================================================================= */\n/* ======================================================================= */\n\n\n/* ================================= */\n/* set invisiblity when not rendered */\n/* ================================= */\ni-input:not(.itag-rendered) {\n /* don't set visibility to hidden --> you cannot set a focus on those items */\n opacity: 0 !important;\n position: absolute !important;\n left: -9999px !important;\n top: -9999px !important;\n z-index: -1;\n}\n\ni-input:not(.itag-rendered) * {\n opacity: 0 !important;\n}\n/* ================================= */\ni-input {\n margin: 0;\n display: inline-block;\n width: 12em;\n position: relative;\n vertical-align: middle;\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n\ni-input > input {\n color: inherit;\n font: inherit;\n margin: 0;\n padding: 0.5em 0.6em;\n display: inline-block;\n border: 1px solid #ccc;\n box-shadow: inset 0 1px 3px #ddd;\n border-radius: 4px;\n line-height: normal;\n width: 100%; /* within the i-input always 100% */\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n\nhtml i-input[disabled] > input {\n cursor: not-allowed;\n background-color: #eaeded;\n color: #cad2d3;\n}\n\ni-input > input::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\n\n\ni-input > input:focus,\ni-input.focussed > input {\n outline: 0;\n outline: thin dotted \\9; /* IE6-9 */\n border-color: #129FEA;\n}\n\ni-input[readonly=\"true\"] > input {\n background: #eee; /* menu hover bg color */\n color: #777; /* menu text color */\n border-color: #ccc;\n}\n\ni-input > input:focus:invalid {\n color: #b94a48;\n border-color: #ee5f5b;\n}\n\ni-input > input:focus:invalid:focus {\n border-color: #e9322d;\n}\n\ni-input.i-rounded > input {\n border-radius: 2em;\n padding: 0.5em 1em;\n}\n\ni-input.i-input-1 {\n width: 100%;\n}\ni-input.i-input-2-3 {\n width: 66%;\n}\ni-input.i-input-1-2 {\n width: 50%;\n}\ni-input.i-input-1-3 {\n width: 33%;\n}\ni-input.i-input-1-4 {\n width: 25%;\n}\n\n@media only screen and (max-width : 480px) {\n i-input {\n margin-bottom: 0.3em;\n display: block;\n }\n}\n\n"; (require("/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify"))(css); module.exports = css;
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],5942:[function(require,module,exports){
module.exports = function (window) {
"use strict";
require('./css/i-input.css');
require('itags.core')(window);
var itagName = 'i-input', // <-- define your own itag-name here
ITSA = window.ITSA,
Event = ITSA.Event,
Itag, IFormElement;
if (!window.ITAGS[itagName]) {
IFormElement = require('i-formelement')(window);
Event.before(itagName+':manualfocus', function(e) {
// the i-select itself is unfocussable, but its button is
// we need to patch `manualfocus`,
// which is emitted on node.focus()
// a focus by userinteraction will always appear on the button itself
// so we don't bother that
var element = e.target;
e.preventDefault();
element.itagReady().then(
function() {
var input = element.getElement('input');
input && input.focus(true);
}
);
});
Event.after('valuechange', function(e) {
var newValue = e.value,
element = e.target.getParent(),
model = element.model,
prevValue = model.value;
model.value = newValue;
/**
* Emitted when a the i-select changes its value
*
* @event i-select:valuechange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the i-input element
* @param e.prevValue {String}
* @param e.newValue {String}
* @since 0.1
*/
element.emit('valuechange', {
prevValue: prevValue,
newValue: newValue
});
}, 'i-input > input');
Itag = IFormElement.subClass(itagName, {
attrs: {
'i-prop': 'string',
'reset-value': 'string',
'placeholder': 'string',
'readonly': 'boolean'
},
init: function() {
var element = this,
designNode = element.getItagContainer(),
value = designNode.getText();
element.defineWhenUndefined('value', value)
// set the reset-value to the inital-value in case `reset-value` was not present
.defineWhenUndefined('reset-value', value);
},
render: function() {
this.setHTML('<input value="'+this.model.value+'" />');
},
sync: function() {
var element = this,
model = element.model,
input = element.getElement('>input');
// it is safe to use setValue --> when the content hasn't changed, `setValue` doesn't do anything
input.setValue(model.value);
// model.placeholder && input.setAttr('placeholder', model.placeholder, true);
// model['reset-value'] && input.setAttr('reset-value', model['reset-value'], true);
},
currentToReset: function() {
var model = this.model;
model['reset-value'] = model.value;
},
reset: function() {
var model = this.model;
model.value = model['reset-value'];
}
});
window.ITAGS[itagName] = Itag;
}
return window.ITAGS[itagName];
};
},{"./css/i-input.css":5941,"i-formelement":5940,"itags.core":5953}],5943:[function(require,module,exports){
module.exports = function (window) {
"use strict";
require('itags.core')(window);
var itagName = 'i-label', // <-- define your own itag-name here
DOCUMENT = window.document,
ITSA = window.ITSA,
Event = ITSA.Event,
Itag;
if (!window.ITAGS[itagName]) {
Event.after('tap', function(e) {
var labelNode = e.target,
iform = labelNode.inside('i-form'),
selector, focusNode;
if (iform && iform.model['active-labels']) {
selector = iform.getFocusManagerSelector(),
focusNode = labelNode.next(selector, iform);
focusNode && focusNode.focus();
}
}, 'i-label');
Itag = DOCUMENT.createItag(itagName, {
attrs: {
content: 'string'
},
init: function() {
var element = this,
designNode = element.getItagContainer(),
content = designNode.getHTML();
// when initializing: make sure NOT to overrule model-properties that already
// might have been defined when modeldata was boundend. Therefore, use `defineWhenUndefined`
// element.defineWhenUndefined('someprop', somevalue); // sets element.model.someprop = somevalue; when not defined yet
element.defineWhenUndefined('content', content);
// make the form wait to show until this element is rendered:
element.setAttr('itag-formwait', 'true', true);
if (element.getParent()) {
// already in the dom --> we can encapsulate
element.encapsulate();
}
else {
element.selfOnceAfter('UI:nodeinsert', element.encapsulate.bind(element));
}
// set the inner-content of the label will be done when syncing
},
encapsulate: function() {
var element = this,
prevSuppress = DOCUMENT._suppressMutationEvents || false,
parentNode = element.getParent(),
parentVNode = parentNode.vnode,
rowNode, rowVNode, vnode, vChildNodes, i, len, absorbed, noblock;
DOCUMENT.suppressMutationEvents(true);
rowNode = parentNode.prepend('<div class="i-formrow"></div>', false, element);
rowVNode = rowNode.vnode;
// now absorb all next nodes, and finish when an i-form-element has been absorbed:
vChildNodes = parentVNode.vChildNodes;
i = vChildNodes.indexOf(rowVNode) + 1;
len = vChildNodes.length;
// i doesn't change: it is len that will decrease, because we absorb items
while (!absorbed && (i<=(--len))) {
vnode = vChildNodes[i];
rowVNode._appendChild(vnode);
absorbed = vnode.isItag && (vnode.tag!=='I-LABEL');
if (absorbed) {
// noblock = vnode.domNode.keepInline();
noblock = (vnode.tag==='I-CHECKBOX');
}
}
absorbed && noblock && rowNode.setClass('itag-noblock');
DOCUMENT.suppressMutationEvents(prevSuppress);
},
decapsulate: function() {
var element = this,
prevSuppress = DOCUMENT._suppressMutationEvents || false,
rowVNode = element.vnode.vParent,
parentVNode = rowVNode.vParent,
vnode;
DOCUMENT.suppressMutationEvents(true);
// set all childnodes to parent:
/*jshint boss:true */
while (vnode=rowVNode.vChildNodes[0]) {
/*jshint boss:false */
parentVNode._insertBefore(vnode, rowVNode);
}
// now remove rowVNode:
parentVNode._removeChild(rowVNode);
DOCUMENT.suppressMutationEvents(prevSuppress);
},
sync: function() {
var element = this;
element.setHTML(element.model.content);
},
destroy: function() {
this.decapsulate();
}
});
window.ITAGS[itagName] = Itag;
}
return window.ITAGS[itagName];
};
},{"itags.core":5953}],5944:[function(require,module,exports){
module.exports = function (window) {
"use strict";
var itagCore = require('itags.core')(window),
itagName = 'i-parcel', // <-- define your own itag-name here
DOCUMENT = window.document,
Itag;
if (!window.ITAGS[itagName]) {
Itag = DOCUMENT.createItag(itagName, {
init: function() {
var element = this,
designNode = element.getItagContainer(),
content = designNode.getHTML();
// when initializing: make sure NOT to overrule model-properties that already
// might have been defined when modeldata was boundend. Therefore, use `defineWhenUndefined`
// element.defineWhenUndefined('someprop', somevalue); // sets element.model.someprop = somevalue; when not defined yet
element.defineWhenUndefined('content', content);
},
sync: function() {
var element = this;
element.setHTML(element.model.content);
}
});
itagCore.setContentVisibility(Itag, true);
window.ITAGS[itagName] = Itag;
}
return window.ITAGS[itagName];
};
},{"itags.core":5953}],5945:[function(require,module,exports){
module.exports = function (window) {
"use strict";
require('itags.core')(window);
var pseudoName = 'reset', // <-- define your own pseudo-name here
itagName = 'i-button#'+pseudoName, // <-- define your own itag-name here
Event = window.ITSA.Event,
Itag, IButton;
if (!window.ITAGS[itagName]) {
IButton = require('i-button')(window);
Event.before(itagName+':manualfocus', function(e) {
// the i-select itself is unfocussable, but its button is
// we need to patch `manualfocus`,
// which is emitted on node.focus()
// a focus by userinteraction will always appear on the button itself
// so we don't bother that
var element = e.target;
e.preventDefault();
element.itagReady().then(
function() {
var button = element.getElement('button');
button && button.focus(true);
}
);
});
Itag = IButton.pseudoClass(pseudoName);
window.ITAGS[itagName] = Itag;
}
return window.ITAGS[itagName];
};
},{"i-button":5935,"itags.core":5953}],5946:[function(require,module,exports){
var css = "/* ======================================================================= */\n/* ======================================================================= */\n/* ======================================================================= */\n/* Definition of itag shadow-css is done by defining a `dummy` css-rule */\n/* for the dummy-element: `itag-css` --> its property (also dummy) `i-tag` /*\n/* will define which itag will be css-shadowed /*\n/* ======================================================================= */\nitag-css {\n i-tag: i-select; /* set the property-value to the proper itag */\n}\n/* ======================================================================= */\n/* ======================================================================= */\n/* ======================================================================= */\n\n\n/* ================================= */\n/* set invisiblity when not rendered */\n/* ================================= */\ni-select:not(.itag-rendered) {\n /* don't set visibility to hidden --> you cannot set a focus on those items */\n opacity: 0 !important;\n position: absolute !important;\n left: -9999px !important;\n top: -9999px !important;\n z-index: -1;\n}\n\ni-select:not(.itag-rendered) * {\n opacity: 0 !important;\n}\n/* ================================= */\n\ni-select {\n margin: 0;\n display: inline-block;\n *display: inline; /*IE 6/7*/\n zoom: 1;\n position: relative;\n vertical-align: middle;\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n\ni-select >span {\n position: relative;\n z-index: 2;\n -webkit-transition: opacity 0.1s;\n -moz-transition: opacity 0.1s;\n -ms-transition: opacity 0.1s;\n -o-transition: opacity 0.1s;\n transition: opacity 0.1s;\n opacity: 0;\n}\n\ni-select >span.i-select-show {\n -webkit-transition: opacity 0.2s;\n -moz-transition: opacity 0.2s;\n -ms-transition: opacity 0.2s;\n -o-transition: opacity 0.2s;\n transition: opacity 0.2s;\n opacity: 1;\n}\n\ni-select >button >span,\ni-select >span,\ni-select >span >span,\ni-select >span >span >span {\n display: block;\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n\ni-select.i-primary >button,\ni-select.i-focussed >button {\n background-color: rgb(0, 120, 231);\n color: #fff;\n}\n\n/*csslint outline-none:false*/\n\ni-select >button {\n -webkit-touch-callout: none;\n -webkit-user-select: none;\n -khtml-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n position: relative;\n padding: 0.5em 0;\n max-width: 8em;\n color: inherit; /* 1 */\n font: inherit; /* 2 */\n margin: 0; /* 3 */\n overflow: visible;\n text-transform: none;\n -webkit-appearance: button; /* 2 */\n line-height: normal;\n white-space: nowrap;\n vertical-align: baseline;\n text-align: center;\n cursor: pointer;\n -webkit-user-drag: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n font-family: inherit;\n font-size: 100%;\n color: #444; /* rgba not supported (IE 8) */\n color: rgba(0, 0, 0, 0.80); /* rgba supported */\n border: 1px solid #999; /*IE 6/7/8*/\n border: none rgba(0, 0, 0, 0); /*IE9 + everything else*/\n background-color: #E6E6E6;\n text-decoration: none;\n border-radius: 2px;\n box-shadow: 0 0 0 1px rgba(0,0,0, 0.15) inset;\n}\n\n/* need .pure-button-active for it gets set when \"enter-pressed\" on button */\ni-select.i-hover >button,\ni-select >button:hover,\ni-select >button:focus {\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#1a000000',GradientType=0);\n background-image: -webkit-gradient(linear, 0 0, 0 100%, from(transparent), color-stop(40%, rgba(0,0,0, 0.05)), to(rgba(0,0,0, 0.10)));\n background-image: -webkit-linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10));\n background-image: -moz-linear-gradient(top, rgba(0,0,0, 0.05) 0%, rgba(0,0,0, 0.10));\n background-image: -o-linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10));\n background-image: linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10));\n}\ni-select >button:focus {\n box-shadow: 0 0 0 1px rgba(0,0,0, 0.6) inset;\n outline: 0;\n}\ni-select.i-active.i-hover >button,\ni-select.i-active >button:hover,\ni-select.i-active.i-hover >button:focus,\ni-select.i-active >button:hover:focus,\ni-select.i-hover >button.pure-button-active, /* need .pure-button-active for it gets set when \"enter-pressed\" on button */\ni-select >button.pure-button-active:hover, /* need .pure-button-active for it gets set when \"enter-pressed\" on button */\ni-select.i-hover >button.pure-button-active:focus, /* need .pure-button-active for it gets set when \"enter-pressed\" on button */\ni-select >button.pure-button-active:hover:focus, /* need .pure-button-active for it gets set when \"enter-pressed\" on button */\ni-select >button.pure-button-active, /* need .pure-button-active for it gets set when \"enter-pressed\" on button */\ni-select >button.pure-button-active:focus, /* need .pure-button-active for it gets set when \"enter-pressed\" on button */\ni-select.i-hover >button:active,\ni-select >button:hover:active,\ni-select.i-hover >button:focus:active,\ni-select >button:hover:focus:active {\n box-shadow: 0 0 0 1px rgba(0,0,0, 0.8) inset, 0 0 6px rgba(0,0,0, 0.4) inset;\n}\n\ni-select.i-disabled >button,\ni-select[disabled=\"true\"] >button,\ni-select.i-disabled >button:active,\ni-select[disabled=\"true\"] >button:active,\ni-select.i-disabled >button.pure-button-active, /* need .pure-button-active for it gets set when \"enter-pressed\" on button */\ni-select[disabled=\"true\"] >button.pure-button-active,\ni-select.i-disabled.i-active >button,\ni-select.i-active[disabled=\"true\"] >button,\ni-select.i-disabled >button:focus,\ni-select[disabled=\"true\"] >button:focus,\ni-select.i-disabled.focussed >button,\ni-select.focussed[disabled=\"true\"] >button,\ni-select.i-disabled >button:hover,\ni-select[disabled=\"true\"] >button:hover {\n border: none;\n background-image: none;\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n filter: alpha(opacity=60);\n -khtml-opacity: 0.60;\n -moz-opacity: 0.60;\n opacity: 0.60;\n cursor: not-allowed;\n box-shadow: 0 0 0 1px rgba(0,0,0, 0.15) inset;\n cursor: default;\n}\n\ni-select.i-rounded >button {\n border-radius: 0.3em;\n}\n\ni-select.i-heavyrounded >button {\n border-radius: 0.5em;\n}\n\ni-select.i-oval >button {\n border-radius: 50%;\n}\n\ni-select.i-halfoval >button {\n border-radius: 25%;\n}\n\ni-select.i-hidden {\n opacity: 0 !important;\n position: absolute !important;\n left: -9999px !important;\n top: -9999px !important;\n z-index: -9;\n}\n\ni-select >button::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\n\ni-select >button >span.btntext {\n margin: 0 1.25em 0 1em;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 8em;\n display: block;\n}\n\ni-select >button >span.pointer {\n border-left: 0.4em solid rgba(0, 0, 0, 0);\n border-right: 0.4em solid rgba(0, 0, 0, 0);\n border-top: 0.5em solid #000;\n right: 0.25em;\n position: absolute;\n bottom: 0.2em;\n}\n\ni-select >button.i-nonexpandable span.btntext {\n margin: 0 1em;\n}\n\ni-select >button.i-nonexpandable span.pointer {\n visibility: hidden;\n}\n\ni-select >span >span {\n position: absolute;\n left: 0;\n top: 0;\n cursor: pointer;\n border-style: solid;\n border-width: 0.1em;\n -webkit-border-radius: 0 0 0.3em 0.3em;\n -moz-border-radius: 0 0 0.3em 0.3em;\n border-radius: 0 0 0.3em 0.3em;\n -webkit-box-shadow: 0.3em 0.3em 5px rgba(0,0,0,0.15);\n -moz-box-shadow: 0.3em 0.3em 5px rgba(0,0,0,0.15);\n box-shadow: 0.3em 0.3em 5px rgba(0,0,0,0.15);\n}\n\ni-select >span >span >span {\n font-size: 1.2em;\n padding: 0 0 0.3em;\n list-style: none;\n margin: 0;\n}\n\ni-select option {\n padding: 0.25em 0.7em;\n white-space: nowrap;\n}\n\ni-select option.focussed {\n background-color: #B3D4FF;\n}\n\ni-select option.selected:before {\n content: '*';\n margin-left: -0.7em;\n padding-right: 0.25em;\n}\n\ni-select option:before,\ni-select option:after {\n content: '';\n padding: 0;\n margin: 0;\n}\n\n/* color specification:; */\n\ni-select >span >span {\n background-color: #FFF;\n border-color: #000;\n}\n\ni-select option:hover {\n background-color: #B3D4FF;\n}\n\ni-select.i-focused > button span.pointer,\ni-select.i-primary > button span.pointer {\n border-top: 0.5em solid #FEFEFE;\n}"; (require("/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify"))(css); module.exports = css;
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],5947:[function(require,module,exports){
/**
* Provides several methods that override native Element-methods to work with the vdom.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* <br>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module vdom
* @submodule extend-element
* @class Element
* @since 0.0.1
*/
/*
* attributes:
* value, expanded, invalid-value
*/
require('./css/i-select.css');
var DELAY_BLURCLOSE = 125,
SUPPRESS_DELAY = 175;
module.exports = function (window) {
"use strict";
require('itags.core')(window);
var DEFAULT_INVALID_VALUE = 'choose',
itagName = 'i-select',
ITSA = window.ITSA,
async = ITSA.async,
later = ITSA.later,
Event = ITSA.Event,
HIDDEN = 'itsa-hidden',
SHOW = 'i-select-show',
Itag, IFormElement;
if (!window.ITAGS[itagName]) {
IFormElement = require('i-formelement')(window);
Event.before(itagName+':manualfocus', function(e) {
// the i-select itself is unfocussable, but its button is
// we need to patch `manualfocus`,
// which is emitted on node.focus()
// a focus by userinteraction will always appear on the button itself
// so we don't bother that
var element = e.target;
e.preventDefault();
element.itagReady().then(
function() {
var button = element.getElement('button');
button && button.focus(true);
}
);
});
Event.before('keydown', function(e) {
if (e.keyCode===40) {
// prevent minus windowscroll:
e.preventDefaultContinue();
}
}, 'i-select > button');
Event.before('tap', function(e) {
// prevent nested focusmanager (parent) to refocus on the button:
e._noFocus = true;
}, 'i-select > button');
Event.after(['tap', 'keydown'], function(e) {
var element = e.target.getParent(),
e_type = e.type,
model, ulNode, liNode, inactive;
if ((e_type==='tap') || (e.keyCode===40)) {
model = element.model;
if (e.keyCode===40) {
e.preventDefault();
model.expanded = true;
}
else {
inactive = element.hasData('_suppressClose');
if (inactive) {
console.info('not reacting to '+e_type+'-event: button is in pauzed state');
return;
}
model.expanded = !model.expanded;
if (!model.expanded) {
liNode = element.getElement('span[plugin-fm="true"] >option[fm-defaultitem]');
liNode && liNode.focus();
}
}
if (model.expanded) {
ulNode = element.getElement('span[plugin-fm="true"]');
ulNode && ulNode.focus();
}
if (model.expanded || (e_type==='tap')) {
element.setData('_suppressClose', true);
later(function() {
element.removeData('_suppressClose');
}, SUPPRESS_DELAY);
}
}
}, 'i-select > button');
Event.after(['tap', 'keypress'], function(e) {
var liNode = e.target,
e_type = e.type,
element, index, ulNode, model, inactive;
if ((e_type==='tap') || (e.keyCode===13)) {
element = liNode.inside('i-select');
model = element.model;
// check for model.expanded --> a hidden selectbox might react on an enterpress
if (model.expanded) {
inactive = element.hasData('_suppressClose');
if (inactive) {
console.info('not reacting to '+e_type+'-event: button is in pauzed state');
return;
}
model = element.model;
ulNode = liNode.getParent();
index = ulNode.getAll('option').indexOf(liNode);
model.expanded = false;
model.value = index+1;
if (e_type==='tap') {
element.setData('_suppressClose', true);
later(function() {
element.removeData('_suppressClose');
}, SUPPRESS_DELAY);
ulNode = element.getElement('span[plugin-fm="true"]');
ulNode && ulNode.focus();
}
// prevent that the focus will be reset to the focusmanager
// when re-synced --> we want the focus on the button:
ulNode.removeClass('focussed');
async(function() {
element.focus();
});
}
}
}, 'i-select span[plugin-fm="true"] > option');
Event.defineEvent(itagName+':valuechange').unPreventable();
Event.after(itagName+':change', function(e) {
var element = e.target,
prevValue = element.getData('i-select-value'),
model = element.model,
newValue = model.value,
markValue;
if (prevValue!==newValue) {
markValue = newValue - 1;
/**
* Emitted when a the i-select changes its value
*
* @event i-select:valuechange
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the i-select element
* @param e.prevValue {Number} the selected item, starting with 1
* @param e.newValue {Number} the selected item, starting with 1
* @param e.buttonText {String} the text that will appear on the button
* @param e.listText {String} the text as it is in the list
* @since 0.1
*/
element.emit('valuechange', {
prevValue: prevValue,
newValue: newValue,
buttonText: model.buttonTexts[markValue] || model.items[markValue],
listText: model.items[markValue]
});
}
element.setData('i-select-value', newValue);
});
Itag = IFormElement.subClass(itagName, {
/*
*
* @property attrs
* @type Object
* @since 0.0.1
*/
attrs: {
expanded: 'boolean',
disabled: 'boolean',
value: 'string',
'i-prop': 'string',
'invalid-value': 'string',
'reset-value': 'string'
},
/**
* Redefines the childNodes of both the vnode as well as its related dom-node. The new
* definition replaces any previous nodes. (without touching unmodified nodes).
*
* Syncs the new vnode's childNodes with the dom.
*
* @method init
* @chainable
* @since 0.0.1
*/
init: function() {
var element = this,
designNode = element.getItagContainer(),
itemNodes = designNode.getAll('>option'),
items = [],
buttonTexts = [],
value = element.model.value;
itemNodes.forEach(function(node, i) {
var header = node.getElement('span[is="button"]');
if (header) {
buttonTexts[i] = header.getHTML();
}
items[items.length] = node.getHTML(header);
});
element.defineWhenUndefined('items', items)
.defineWhenUndefined('buttonTexts', buttonTexts)
// set the reset-value to the inital-value in case `reset-value` was not present
.defineWhenUndefined('reset-value', value);
// store its current value, so that valueChange-event can fire:
element.setData('i-select-value', value);
element.cleanupEvents();
element.setupEvents();
},
/**
* Redefines the childNodes of both the vnode as well as its related dom-node. The new
* definition replaces any previous nodes. (without touching unmodified nodes).
*
* Syncs the new vnode's childNodes with the dom.
*
* @method render
* @chainable
* @since 0.0.1
*/
render: function() {
var element = this,
content;
// building the template of the itag:
content = '<button><span class="pointer"></span><span class="btntext"></span></button>';
// first: outerdiv which will be relative positioned
// next: innerdiv which will be absolute positioned
// also: hide the container by default --> updateUI could make it shown
content += '<span class="itsa-hidden">' +
'<span>'+
'<span plugin-fm="true" fm-manage="option" fm-keyup="38" fm-keydown="40" fm-noloop="true"></span>';
'</span>'+
'</span>';
// set the content:
element.setHTML(content);
},
cleanupEvents: function() {
this._outsideListener && this._outsideListener.detach();
},
currentToReset: function() {
var model = this.model;
model['reset-value'] = model.value;
},
reset: function() {
var model = this.model;
model.value = model['reset-value'];
},
setupEvents: function() {
var element = this;
// because the tapoutside event is not set through element.salfAfter, we need to detach the event when needed:
element._outsideListener = Event.after('tapoutside', function(e) {
async(function() {
if (!element.hasData('_suppressClose') && !element.contains(e.sourceTarget)) {
element.model.expanded = false;
}
});
}, 'i-select');
element.selfAfter('blurnode', function() {
// at the end of the eventstack: give `blurnode` a way to set the '_suppressClose'-data when needed
// need a bit more time because there is time inbetween the blur vs click events
later(function() {
if (!element.hasData('_suppressClose')) {
element.model.expanded = false;
}
}, DELAY_BLURCLOSE);
});
},
/**
* Redefines the childNodes of both the vnode as well as its related dom-node. The new
* definition replaces any previous nodes. (without touching unmodified nodes).
*
* Syncs the new vnode's childNodes with the dom.
*
* @method _setChildNodes
* @param newVChildNodes {Array} array with vnodes which represent the new childNodes
* @private
* @chainable
* @since 0.0.1
*/
sync: function() {
// inside sync, YOU CANNOT change attributes which are part of `attrs` !!!
// those actions will be ignored.
// BE CAREFUL to start async actions here:
// be aware that before ending, this method can run again
// if you do, then make sure to handle possible running
// async actions well !!!
var element = this,
model = element.model,
items = model.items || [],
buttonTexts = model.buttonTexts,
value = model.value,
item, content, buttonText, len, i, markValue,
button, container, itemsContainer, hiddenTimer;
len = items.length;
(value>len) && (value=0);
markValue = value - 1;
if (value>0) {
buttonText = buttonTexts[markValue] || items[markValue];
}
else {
buttonText = model['invalid-value'] || DEFAULT_INVALID_VALUE;
}
// rebuild the button:
button = element.getElement('button');
button.toggleClass('i-nonexpandable', (len<2));
button.getElement('span.btntext').setHTML(buttonText);
container = element.getElement('>span');
if (model.expanded && !model.disabled && !element.hasClass('i-disabled') && (len>1)) {
hiddenTimer = container.getData('_hiddenTimer');
hiddenTimer && hiddenTimer.cancel();
container.setClass(SHOW);
container.removeClass(HIDDEN);
}
else {
container.removeClass(SHOW);
// hide the layer completely: we need to access anything underneath:
hiddenTimer = later(function() {
container.setClass(HIDDEN);
}, 110);
container.setData('_hiddenTimer', hiddenTimer);
}
itemsContainer = element.getElement('span[plugin-fm="true"]');
content = '';
for (i=0; i<len; i++) {
item = items[i];
content += '<option'+((i===markValue) ? ' class="selected" fm-defaultitem="true"' : '')+'>'+item+'</option>';
}
// set the items:
itemsContainer.setHTML(content, true);
},
destroy: function() {
this.cleanupEvents();
}
});
window.ITAGS[itagName] = Itag;
}
return window.ITAGS[itagName];
};
},{"./css/i-select.css":5946,"i-formelement":5940,"itags.core":5953}],5948:[function(require,module,exports){
var css = "/* ======================================================================= */\n/* ======================================================================= */\n/* ======================================================================= */\n/* Definition of itag shadow-css is done by defining a `dummy` css-rule */\n/* for the dummy-element: `itag-css` --> its property (also dummy) `i-tag` /*\n/* will define which itag will be css-shadowed /*\n/* ======================================================================= */\nitag-css {\n i-tag: i-statusbar; /* set the property-value to the proper itag */\n}\n/* ======================================================================= */\n/* ======================================================================= */\n/* ======================================================================= */\n\n\n/* ================================= */\n/* set invisiblity when not rendered */\n/* ================================= */\ni-statusbar:not(.itag-rendered) {\n /* don't set visibility to hidden --> you cannot set a focus on those items */\n opacity: 0 !important;\n position: absolute !important;\n left: -9999px !important;\n top: -9999px !important;\n z-index: -1;\n}\n\ni-statusbar:not(.itag-rendered) * {\n opacity: 0 !important;\n}\n/* ================================= */\n\ni-statusbar {\n color: #222;\n margin: 0;\n padding: 0;\n display: block;\n position: fixed;\n left: 0;\n bottom: 0;\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n width: 100%;\n line-height: 1.5em;\n vertical-align: middle;\n padding: 2px 0.5em 0;\n border-top: solid 1px #BBB;\n background: rgb(238,238,238);\n background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2VlZWVlZSIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjExJSIgc3RvcC1jb2xvcj0iI2VlZWVlZSIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNkZGRkZGQiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+);\n background: -moz-linear-gradient(top, rgba(238,238,238,1) 0%, rgba(238,238,238,1) 11%, rgba(221,221,221,1) 100%);\n background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(238,238,238,1)), color-stop(11%,rgba(238,238,238,1)), color-stop(100%,rgba(221,221,221,1)));\n background: -webkit-linear-gradient(top, rgba(238,238,238,1) 0%,rgba(238,238,238,1) 11%,rgba(221,221,221,1) 100%);\n background: -o-linear-gradient(top, rgba(238,238,238,1) 0%,rgba(238,238,238,1) 11%,rgba(221,221,221,1) 100%);\n background: -ms-linear-gradient(top, rgba(238,238,238,1) 0%,rgba(238,238,238,1) 11%,rgba(221,221,221,1) 100%);\n background: linear-gradient(to bottom, rgba(238,238,238,1) 0%,rgba(238,238,238,1) 11%,rgba(221,221,221,1) 100%);\n}\n\ni-statusbar >span {\n white-space: nowrap;\n}\n\ni-statusbar >span:first-child {\n overflow: hidden;\n text-overflow: ellipsis;\n text-align: left;\n}\n\ni-statusbar >span:last-child {\n text-align: right;\n float: right;\n}\n\ni-statusbar >span i-button,\ni-statusbar >span button.pure-button,\ni-statusbar >span button {\n margin: 0;\n background-color: #FFF;\n padding: 0.5em 0.6em 0.1em;\n color: #222;\n font-size: 0.8em;\n box-shadow: 0 0 0 1px rgba(0,0,0, 0.15) inset;\n}\n\ni-statusbar >span i-button.i-primary,\ni-statusbar >span button.pure-button-primary {\n background-color: #FFF;\n color: #222;\n}"; (require("/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify"))(css); module.exports = css;
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],5949:[function(require,module,exports){
module.exports = function (window) {
"use strict";
require('itags.core')(window);
require('./css/i-statusbar.css');
var itagName = 'i-statusbar', // <-- define your own itag-name here
DOCUMENT = window.document,
ITSA = window.ITSA,
DEFAULT_READY = 'ready',
MESSAGE_LEVELS = {
'message': 1,
'warning': 2,
'error': 3
},
MESSAGE_HASHES = {
'message': 'messages',
'warning': 'warnings',
'error': 'errors'
},
MESSAGE_HASHES_NR = {
1: 'messages',
2: 'warnings',
3: 'errors'
},
FOLLOWUP_DELAY = 200,
Itag;
if (!window.ITAGS[itagName]) {
Itag = DOCUMENT.createItag(itagName, {
attrs: {
events: 'string',
readyContent: 'string'
},
init: function() {
var element = this;
element.setData('currentMessageLevel', 0);
element.setData('messages', []);
element.setData('warnings', []);
element.setData('errors', []);
element.defineWhenUndefined('readyContent', DEFAULT_READY);
element.defineWhenUndefined('content', element.model.readyContent || '');
// ITSA.Event.after('tap', element.finishMessage.bind(element), 'button'); // works
// ITSA.Event.after('tap', element.aa.bind(element), 'button');
// element.after('tap', element.finishMessage.bind(element), 'button');
element.selfAfter('tap', element.finishMessage.bind(element), 'button');
},
setupListener: function() {
var element = this,
events = element.model.events.split(',');
if (events.length>0) {
element.setData('_listener', element.before(events, element.processMessage.bind(element)));
}
},
detachListener: function() {
var element = this,
listener = this.getData('_listener');
listener && listener.detach();
element.removeData('_listener');
},
render: function() {
this.setHTML('<span></span><span></span>');
},
sync: function() {
var element = this,
model = element.model,
spans = element.getAll('>span'),
firstSpan = spans[0],
lastSpan = spans[1],
prevEvents = element.getData('_prevEvents'),
footer = model.footer,
events = model.events;
if (events!==prevEvents) {
element.detachListener();
element.setupListener();
element.setData('_prevEvents', events);
}
firstSpan.setHTML(model.content);
if (footer) {
lastSpan.setHTML(model.footer);
}
lastSpan.toggleClass('itsa-hidden', !footer);
},
finishMessage: function(e) {
// fulfill with a containerNode --> the same as `dialog` does:
var element = this,
buttonNode = e.target,
model = element.model,
containerNode = DOCUMENT.createElement('div');
containerNode.setHTML(model.content);
containerNode.append(buttonNode.getOuterHTML());
model.messagePromise.fulfill(containerNode);
},
processMessage: function(e) {
var element = this,
messagePromise = e.messagePromise,
type = e.type,
level = MESSAGE_LEVELS[type],
list = element.getData([MESSAGE_HASHES[type]]);
// only process here: no further:
e.halt();
list[list.length] = messagePromise;
messagePromise.finally(
function() {
list.remove(messagePromise);
// handle the next message (if there)
element.handleMessage(true);
}
);
(level>element.getData('currentMessageLevel')) && element.handleMessage(!element.isWaiting(), level);
},
isWaiting: function() {
return (this.getData('currentMessageLevel')===0);
},
handleMessage: function(delay, level) {
var element = this,
model = element.model,
messagePromise;
if (!level) {
// search level
if (element.getData('errors').length>0) {
level = 3;
}
else if (element.getData('warnings').length>0) {
level = 2;
}
else if (element.getData('messages').length>0) {
level = 1;
}
}
if (!level || (element.getData([MESSAGE_HASHES_NR[level]]).length===0)) {
// DO NOT make messagePromise null: it sould be there as return value
// of the last message
element.setData('currentMessageLevel', 0);
model.content = model.readyContent;
model.footer = null;
return;
}
element.setData('currentMessageLevel', level);
// now process the highest message
messagePromise = element.getData([MESSAGE_HASHES_NR[level]])[0];
if (delay) {
model.visible = false;
ITSA.later(element.showMessage.bind(element, messagePromise), FOLLOWUP_DELAY);
}
else {
ITSA.async(element.showMessage.bind(element, messagePromise));
}
},
showMessage: function(messagePromise) {
var model = this.model;
model.messagePromise = messagePromise;
model.content = messagePromise.content;
model.footer = messagePromise.footer;
model.visible = true;
},
destroy: function() {
var element = this;
element.detachListeners();
element.removeData(); // all data
}
});
window.ITAGS[itagName] = Itag;
}
return window.ITAGS[itagName];
};
},{"./css/i-statusbar.css":5948,"itags.core":5953}],5950:[function(require,module,exports){
var css = "/* ======================================================================= */\n/* ======================================================================= */\n/* ======================================================================= */\n/* Definition of itag shadow-css is done by defining a `dummy` css-rule */\n/* for the dummy-element: `itag-css` --> its property (also dummy) `i-tag` /*\n/* will define which itag will be css-shadowed /*\n/* ======================================================================= */\nitag-css {\n i-tag: i-tabpane; /* set the property-value to the proper itag */\n}\n/* ======================================================================= */\n/* ======================================================================= */\n/* ======================================================================= */\n\n\n/* ================================= */\n/* set invisiblity when not rendered */\n/* ================================= */\ni-tabpane:not(.itag-rendered) {\n /* don't set visibility to hidden --> you cannot set a focus on those items */\n opacity: 0 !important;\n position: absolute !important;\n left: -9999px !important;\n top: -9999px !important;\n z-index: -1;\n}\n\ni-tabpane:not(.itag-rendered) * {\n opacity: 0 !important;\n}\n/* ================================= */\n\ni-tabpane {\n /* make it accept width and height by swith from :inline\" to \"inline-block\"*/\n display: inline-block;\n *display: block;\n *zoom: 1;\n}\n\ni-tabpane >ul {\n margin:0;\n padding:0;\n list-style:none;\n height: 1.9em;\n overflow: hidden;\n}\n\ni-tabpane >ul li div {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n opacity: 0.6;\n margin: 0;\n padding: 0;\n border: none;\n}\n\ni-tabpane >ul li:hover div {\n opacity: 0.8;\n}\n\ni-tabpane >ul li.pure-button-active div,\ni-tabpane >ul li:active div {\n opacity: 1;\n}\n\ni-tabpane >ul li {\n display: inline-block;\n *display: inline; /* IE */\n *zoom: 1; /* IE */\n margin-right: 0.25em;\n box-shadow: 0 0 0 1px rgba(0,0,0, 0.15) inset;\n}\n\ni-tabpane >ul li.pure-button {\n display: inline-block;\n *display: inline; /* IE */\n *zoom: 1; /* IE */\n margin-right: 0.2em;\n border-radius: 2px 2px 0 0;\n border-bottom: none;\n}\n\ni-tabpane >div {\n height: 100%;\n margin-top: -2.18em;\n padding-top: 2.18em;\n}\n\ni-tabpane >div >div.container {\n border: 1px solid #2647a0;\n border-top: 5px solid #2647a0;\n padding: 0.25em 0.5em;\n height: 100%;\n width: 100%;\n overflow: scroll;\n}\n"; (require("/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify"))(css); module.exports = css;
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],5951:[function(require,module,exports){
/*
* attributes:
* value, expanded, primary-button
*/
require('./css/i-tabpane.css');
module.exports = function (window) {
"use strict";
var itagName = 'i-tabpane',
itagCore = require('itags.core')(window),
DOCUMENT = window.document,
ITSA = window.ITSA,
Event = ITSA.Event,
SUPPRESS_DELAY = 150, // to prevent flickr due to focusmanager when clicked on li-elements
Itag;
if (!window.ITAGS[itagName]) {
Event.before(itagName+':manualfocus', function(e) {
// the i-select itself is unfocussable, but its button is
// we need to patch `manualfocus`,
// which is emitted on node.focus()
// a focus by userinteraction will always appear on the button itself
// so we don't bother that
var element = e.target;
e.preventDefault();
element.itagReady().then(
function() {
var ul = element.getElement('>ul');
ul && ul.focus();
}
);
});
Event.after('focus', function(e) {
var node = e.target,
ul = node.getParent(),
element = ul.getParent(),
model = element.model,
liNodes, newPane;
liNodes = ul.getAll('li');
newPane = liNodes.indexOf(node) + 1;
if (!element.hasData('_suppressTabSwitch')) {
model.pane = newPane;
element.setData('_suppressTabSwitch', true);
ITSA.later(function() {
element.removeData('_suppressTabSwitch');
/*jshint boss:true */
if (newPane=element.getData('_newPane')) {
/*jshint boss:false */
model.pane = newPane;
element.removeData('_newPane');
}
}, SUPPRESS_DELAY);
}
else {
element.setData('_newPane', newPane);
}
}, 'i-tabpane > ul li');
Itag = DOCUMENT.createItag(itagName, {
/*
* Internal hash containing all DOM-events that are listened for (at `document`).
*
* @property DOMEvents
* @default {}
* @type Object
* @private
* @since 0.0.1
*/
attrs: {
pane: 'number',
'reset-value': 'string',
'i-prop': 'string'
},
/**
* Redefines the childNodes of both the vnode as well as its related dom-node. The new
* definition replaces any previous nodes. (without touching unmodified nodes).
*
* Syncs the new vnode's childNodes with the dom.
*
* @method init
* @chainable
* @since 0.0.1
*/
init: function() {
var element = this,
designNode = element.getItagContainer(),
itemNodes = designNode.getAll('>section'),
model = element.model,
pane = model.pane,
panes = [],
tabs = [];
itemNodes.forEach(function(node, i) {
var header = node.getElement('span[is="tab"]');
if (header) {
tabs[i] = header.getHTML();
}
else {
tabs[i] = ' ';
}
panes[panes.length] = node.getHTML(header, true);
});
element.defineWhenUndefined('panes', panes)
.defineWhenUndefined('tabs', tabs)
// set the reset-value to the inital-value in case `reset-value` was not present
.defineWhenUndefined('reset-value', pane);
// store its current value, so that valueChange-event can fire:
element.setData('i-select-pane', pane);
},
/**
* Redefines the childNodes of both the vnode as well as its related dom-node. The new
* definition replaces any previous nodes. (without touching unmodified nodes).
*
* Syncs the new vnode's childNodes with the dom.
*
* @method render
* @chainable
* @since 0.0.1
*/
render: function() {
var element = this,
content;
// note: the container wil excist of a div inside a div --> to make the css work (100% height within i-tabpane)
content = '<ul plugin-fm="true" fm-manage="li" fm-keyup="37" fm-keydown="39" fm-noloop="true"></ul>';
content += '<div><div class="container"></div></div>';
// set the other content:
element.setHTML(content);
},
currentToReset: function() {
var model = this.model;
model['reset-value'] = model.pane;
},
reset: function() {
var model = this.model;
model.pane = model['reset-value'];
},
/**
* Redefines the childNodes of both the vnode as well as its related dom-node. The new
* definition replaces any previous nodes. (without touching unmodified nodes).
*
* Syncs the new vnode's childNodes with the dom.
*
* @method _setChildNodes
* @param newVChildNodes {Array} array with vnodes which represent the new childNodes
* @private
* @chainable
* @since 0.0.1
*/
sync: function() {
// inside sync, YOU CANNOT change attributes which are part of `attrs` !!!
// those actions will be ignored.
// BE CAREFUL to start async actions here:
// be aware that before ending, this method can run again
// if you do, then make sure to handle possible running
// async actions well !!!
var element = this,
model = element.model,
panes = model.panes,
pane = model.pane,
tabs = model.tabs,
len = tabs.length,
navContainer = element.getElement('>ul'),
container = element.getElement('div.container'),
content = '',
i, tabItem, index;
index = pane - 1;
for (i=0; i<len; i++) {
tabItem = tabs[i];
if (i===index) {
content += '<li class="pure-button pure-button-active" fm-defaultitem="true"><div>'+tabItem+'</div></li>';
}
else {
content += '<li class="pure-button"><div>'+tabItem+'</div></li>';
}
}
// set the tabs:
navContainer.setHTML(content, true);
// set the content:
// CANNOT be done silently: there can be itags within the pane
container.setHTML(panes[index]);
}
});
itagCore.setContentVisibility(Itag, true);
window.ITAGS[itagName] = Itag;
}
return window.ITAGS[itagName];
};
},{"./css/i-tabpane.css":5950,"itags.core":5953}],5952:[function(require,module,exports){
var css = "span.itag-data {\n display: none !important;\n}"; (require("/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify"))(css); module.exports = css;
},{"/Volumes/Data/Marco/Documenten Marco/GitHub/itags.contributor/node_modules/cssify":1}],5953:[function(require,module,exports){
(function (global){
/*jshint proto:true */
/**
* Provides several methods that override native Element-methods to work with the vdom.
*
*
* <i>Copyright (c) 2015 ITSA - https://github.com/itags</i>
* <br>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module itags.core
* @class itagCore
* @since 0.0.1
*/
"use strict";
require('./css/itags.core.css');
var NAME = '[itags.core]: ',
ITSA = require('itsa'),
createHashMap = ITSA.createHashMap,
async = ITSA.async,
Event = ITSA.Event,
Classes = ITSA.Classes,
CLASS_ITAG_RENDERED = 'itag-rendered',
DEFAULT_CHAIN_INIT = true,
DEFAULT_CHAIN_DESTROY = true,
NODE = 'node',
REMOVE = 'remove',
INSERT = 'insert',
CHANGE = 'change',
ATTRIBUTE = 'attribute',
NODE_REMOVE = NODE+REMOVE,
NODE_INSERT = NODE+INSERT,
NODE_CONTENT_CHANGE = NODE+'content'+CHANGE,
ATTRIBUTE_REMOVE = ATTRIBUTE+REMOVE,
ATTRIBUTE_CHANGE = ATTRIBUTE+CHANGE,
ATTRIBUTE_INSERT = ATTRIBUTE+INSERT,
/**
* Internal hash containing the names of members which names should be transformed
*
* @property ITAG_METHODS
* @default {init: '_initUI', sync: '_syncUI', destroy: '_destroyUI', attrs: '_attrs'}
* @type Object
* @protected
* @since 0.0.1
*/
ITAG_METHODS = createHashMap({
init: '_initUI',
render: '_renderUI',
sync: '_syncUI',
destroy: '_destroyUI',
attrs: '_attrs'
}),
// ITAG_METHOD_VALUES must match previous ITAG_METHODS's values!
ITAG_METHOD_VALUES = createHashMap({
_initUI: true,
_renderUI: true,
_syncUI: true,
_destroyUI: true,
_attrs: true
}),
NOOP = function() {};
module.exports = function (window) {
// make ITSA available as global, so we can use it in all other itag-modules:
window.ITSA = ITSA;
var DOCUMENT = window.document,
PROTOTYPE_CHAIN_CAN_BE_SET = arguments[1], // hidden feature, used by unit-test
RUNNING_ON_NODE = (typeof global !== 'undefined') && (global.window!==window),
PROTO_SUPPORTED = !!Object.__proto__,
BINDING_LIST = {},
itagCore, PROTECTED_MEMBERS, EXTRA_BASE_MEMBERS,
ATTRIBUTE_EVENTS, manageFocus, mergeFlat;
/*jshint boss:true */
if (itagCore=window._ItagCore) {
/*jshint boss:false */
return itagCore; // itagCore was already defined
}
/**
* Internal hash containing all ITAG-Class definitions.
*
* @property ITAGS
* @type Object
* @for window
* @since 0.0.1
*/
Object.protectedProp(window, 'ITAGS', {}); // for the ProtoConstructors
/**
* Base properties for every Itag-class
*
*
* @property EXTRA_BASE_MEMBERS
* @type Object
* @protected
* @for ItagBaseClass
* @since 0.0.1
*/
EXTRA_BASE_MEMBERS = {
/**
* Calls `_destroyUI` on through the class-chain on every level (bottom-up).
* _destroyUI gets defined when the itag defines `destroy` --> transformation under the hood.
*
* Syncs the new vnode's childNodes with the dom.
*
* @method destroyUI
* @param constructor {Class} the Class which belongs with the itag
* @param [reInitialize=false] {Boolean} whether the destruction comes from a `re-initialize`-call. For internal usage.
* @chainable
* @since 0.0.1
*/
destroyUI: function(constructor, reInitialize) {
var instance = this,
vnode = instance.vnode,
superDestroy, observer;
if (vnode.ce_initialized && (reInitialize || vnode.removedFromDOM) && !vnode.ce_destroyed) {
if (!reInitialize) {
observer = instance.getData('_observer');
instance.model.unobserve(observer);
instance.removeData('_observer');
}
superDestroy = function(constructor) {
var classCarierBKP = instance.__classCarier__;
// don't call `hasOwnProperty` directly on obj --> it might have been overruled
Object.prototype.hasOwnProperty.call(constructor.prototype, '_destroyUI') && constructor.prototype._destroyUI.call(instance);
if (constructor.$$chainDestroyed) {
instance.__classCarier__ = constructor.$$super.constructor;
superDestroy(constructor.$$super.constructor);
}
classCarierBKP = instance.__classCarier__;
};
superDestroy(constructor || instance.constructor);
itagCore.destroyPlugins(instance);
instance.detachAll();
// DO NOT set model to null --> it might be refered to asynchronously
// We don't need to bother: the node gets out of the dom and will really be destroyed after
// 1 minute: because no-one needs it, the GC should clean up model when no longer needed
}
reInitialize || Object.protectedProp(vnode, 'ce_destroyed', true);
return instance;
},
/**
* Unitializer for itags. Calls the `_init`-method through the whole chain (top-bottom).
* _initUI() is set for each `init`-member --> transformed under the hood.
*
* @method initUI
* @param constructor {Class} the Class which belongs with the itag
* @param [reInitialize=false] {Boolean} whether the initialization comes from a `re-initialize`-call. For internal usage.
* @chainable
* @since 0.0.1
*/
initUI: function(constructor, reInitialize) {
var instance = this,
vnode = instance.vnode,
superInit, serverModel;
if ((reInitialize || !vnode.ce_initialized) && !vnode.removedFromDOM && !vnode.ce_destroyed) {
superInit = function(constructor) {
var classCarierBKP = instance.__classCarier__;
if (constructor.$$chainInited) {
instance.__classCarier__ = constructor.$$super.constructor;
superInit(constructor.$$super.constructor);
}
classCarierBKP = instance.__classCarier__;
// don't call `hasOwnProperty` directly on obj --> it might have been overruled
Object.prototype.hasOwnProperty.call(constructor.prototype, '_initUI') && constructor.prototype._initUI.call(instance);
};
if (!reInitialize) {
// First time init.
// If already rendered on the server:
// bind the stored json-data on the property `model`:
if (instance.hasClass(CLASS_ITAG_RENDERED)) {
// already rendered on the server
if (!RUNNING_ON_NODE) {
serverModel = itagCore.extractModel(instance);
if (serverModel && !vnode.ce_boundModel) {
instance.model = serverModel;
}
}
Object.protectedProp(vnode, 'ce_designNode', itagCore.extractContent(instance));
}
else {
Object.protectedProp(vnode, 'ce_designNode', itagCore.extractContent(instance, true));
}
}
superInit(constructor || instance.constructor);
}
return instance;
},
/**
* Does the one-time initial rendering: is succeeded with syncUI
*
* @method renderUI
* @param [reInitialize=false] {Boolean} whether the renderUI comes from a `re-initialize`-call. For internal usage.
* @chainable
* @since 0.0.1
*/
renderUI: function(reInitialize) {
var instance = this,
vnode = instance.vnode;
if ((reInitialize || !vnode.ce_initialized) && !vnode.removedFromDOM && !vnode.ce_destroyed) {
instance._renderUI();
itagCore.initPlugins(instance);
Object.protectedProp(vnode, 'ce_initialized', true);
}
return instance;
},
getItagContainer: function() {
return this.vnode.ce_designNode;
},
/**
* Flag that tells wether the itag is rendered. If you need to wait for rendering (to continue processing),
* then use `itagReady()`
*
* Syncs the new vnode's childNodes with the dom.
*
* @method isRendered
* @return {Boolean} whether the itag is rendered.
* @since 0.0.1
*/
isRendered: function() {
return !!this.getData('itagRendered');
},
/**
* Flag that tells wether the itag is destoyed.
*
* @method isDestroyed
* @return {Boolean} whether the itag is destroyed.
* @since 0.0.1
*/
isDestroyed: function() {
return !!this.vnode.ce_destroyed;
},
/**
* Promise that gets fulfilled as soon as the itag is rendered.
*
* @method itagReady
* @return {Promise} fulfilled when rendered for the first time.
* @since 0.0.1
*/
itagReady: function() {
var instance = this;
if (!instance.isItag()) {
console.warn('itagReady() invoked on a non-itag element');
return window.Promise.reject('Element is no itag');
}
instance._itagReady || (instance._itagReady=window.Promise.manage());
return instance._itagReady;
},
/**
* Destroys and reinitialises the itag-element.
* No need to use directly, only internal.
*
* @method reInitializeUI
* @param constructor {Class} the Class which belongs with the itag
* @chainable
* @since 0.0.1
*/
reInitializeUI: function(constructor) {
var instance = this,
vnode = instance.vnode;
if (vnode.ce_initialized && !vnode.removedFromDOM && !vnode.ce_destroyed) {
instance.destroyUI(constructor, true)
.initUI(constructor, true)
.renderUI(true)
.syncUI();
}
return instance;
},
/**
* Defines the `key`-property on element.model, but only when is hasn't been defined before.
*
* @method defineWhenUndefined
* @chainable
* @since 0.0.1
*/
defineWhenUndefined: function(key, value) {
var model = this.model;
model[key] || (model[key]=value);
return this;
},
/**
* Syncs the itag, by calling `_syncUI`: the transformed `sync()`-method.
*
* Syncs the new vnode's childNodes with the dom.
*
* @method syncUI
* @chainable
* @since 0.0.1
*/
syncUI: function() {
var instance = this,
attrs = instance._attrs,
vnode = instance.vnode,
stringifiedData, vChildNodes, lastVChild;
if (vnode.ce_initialized && !vnode.removedFromDOM && !vnode.ce_destroyed) {
vnode._setUnchangableAttrs(attrs);
instance._syncUI.apply(instance, arguments);
vnode._setUnchangableAttrs(null);
if (RUNNING_ON_NODE) {
// store the modeldata inside a commentNode at the end of innerHTML:
try {
stringifiedData = JSON.stringify(instance.model);
// we need to patch directly on the vnode --> modification of commentNodes
// have no customized methods on the Element, but they are patchable through Element.vnode:
vChildNodes = vnode.vChildNodes;
lastVChild = vChildNodes[vChildNodes.length-1];
if (lastVChild && (lastVChild.nodeType===8) && (lastVChild.text.startsWith('i-model:{'))) {
// modeldata was already set --> overwrite it
lastVChild.text = 'i-model:'+stringifiedData;
// lastVChild.domNode.nodeValue = unescapeEntities(lastVChild.text);
lastVChild.domNode.nodeValue = lastVChild.text;
}
else {
// insert modeldata
instance.prepend('<!--i-model:'+stringifiedData+'-->');
}
}
catch(e) {
console.warn(e);
}
}
}
return instance;
},
contentHidden: true,
/**
* Invoked after a model is bound. Can be used for further action.
* Not always need to: after this method, `sync` will get invoked.
*
* @method _afterBindModel
* @private
* @chainable
* @since 0.0.1
*/
_afterBindModel: NOOP,
/**
* Internal hash containing the `attrs`-definition which can be set by the itag-declaration.
* This hash is used to determine which properties of `model` need to sync as an attribute.
*
* @property _attrs
* @default {}
* @type Object
* @private
* @since 0.0.1
*/
_attrs: {},
/**
* Transformed from `destroy` --> when `destroy` gets invoked, the instance will invoke `_destroyUI` through the whole chain.
* Defaults to `NOOP`, so that it can be always be invoked.
*
* @method _destroyUI
* @private
* @chainable
* @since 0.0.1
*/
_destroyUI: NOOP,
/**
* Transformed from `init` --> when the instance gets created, the instance will invoke `_initUI` through the whole chain.
* Defaults to `NOOP`, so that it can be always be invoked.
*
* Syncs the new vnode's childNodes with the dom.
*
* @method _initUI
* @private
* @since 0.0.1
*/
_initUI: NOOP,
/**
* Transformed from `render` --> when the instance gets created, the instance will invoke `_renderUI` through the whole chain.
* Defaults to `NOOP`, so that it can be always be invoked.
*
* Syncs the new vnode's childNodes with the dom.
*
* @method _renderUI
* @private
* @since 0.0.1
*/
_renderUI: NOOP,
/**
* Transformed from `sync` --> when `sync` gets invoked, the instance will invoke `_syncUI`.
* Defaults to `NOOP`, so that it can be always be invoked.
*
* @method _syncUI
* @private
* @since 0.0.1
*/
_syncUI: NOOP
};
EXTRA_BASE_MEMBERS.merge(Event.Listener)
.merge(Event._CE_listener);
/**
* Internal hash holding all attribute-mutation events
*
* @property ATTRIBUTE_EVENTS
* @default ['attributeremove', 'attributechange', 'attributeinsert']
* @type Array
* @protected
* @since 0.0.1
*/
ATTRIBUTE_EVENTS = [ATTRIBUTE_REMOVE, ATTRIBUTE_CHANGE, ATTRIBUTE_INSERT];
/**
* Internal hash containing all `protected members` --> the properties that CANNOT be set at the prototype of ItagClasses.
*
* @property PROTECTED_MEMBERS
* @default {
* bindModel: true,
* destroyUI: true,
* initUI: true,
* isRendered: true,
* reInitializeUI: true,
* syncUI: true
* }
* @type Object
* @private
* @since 0.0.1
*/
PROTECTED_MEMBERS = createHashMap();
EXTRA_BASE_MEMBERS.each(function(value, key) {
ITAG_METHOD_VALUES[key] || (key==='_afterBindModel') || (PROTECTED_MEMBERS[key] = true);
});
/**
* Merges all prototype-members of every level in the chain directly on the domElement.
* This needs to be done for browsers which don't support changing __proto__ (like <IE11)
*
* @method mergeFlat
* @param constructor {Class} the Class which belongs with the itag, holding all the members
* @param domElement {HTMLElement} the Element that recieves the members
* @private
* @since 0.0.1
*/
mergeFlat = function(constructor, domElement) {
var prototype = constructor.prototype,
keys, i, name, propDescriptor;
if (domElement.__addedProps__) {
// set before: erase previous properties
domElement.__addedProps__.each(function(value, key) {
delete domElement[key];
});
}
domElement.__addedProps__ = {};
while (prototype !== window.HTMLElement.prototype) {
keys = Object.getOwnPropertyNames(prototype);
/*jshint boss:true */
for (i=0; name=keys[i]; i++) {
/*jshint boss:false */
if (!domElement.__addedProps__[name]) {
propDescriptor = Object.getOwnPropertyDescriptor(prototype, name);
propDescriptor.configurable = true;
// needs configurable, otherwise we cannot delete it when refreshing
Object.defineProperty(domElement, name, propDescriptor);
domElement.__addedProps__[name] = true;
}
}
constructor = constructor.$$super.constructor;
prototype = constructor.prototype;
}
};
itagCore = {
initPlugins: function(domElement) {
var processVChildNodes = function(vnode) {
var vChildren = vnode.vChildren,
len = vChildren.length,
i, vChild, attrs, ns, j, len2, keys, attribute;
for (i=0; i<len; i++) {
vChild = vChildren[i];
/*jshint boss:true */
if (attrs=vChild.attrs) {
/*jshint boss:false */
keys = attrs.keys();
len2 = keys.length;
for (j=0; j<len2; j++) {
attribute = keys[j];
if ((attribute.substr(0, 7)==='plugin-') && (attrs[attribute]==='true')) {
ns = attribute.substr(7);
vChild.domNode.plug(ns);
}
}
}
processVChildNodes(vChild);
}
};
processVChildNodes(domElement.vnode);
},
destroyPlugins: function(domElement) {
var processVChildNodes = function(vnode) {
var vChildren = vnode.vChildren,
len = vChildren.length,
i, vChild, j, len2, ns, keys, plugin;
for (i=0; i<len; i++) {
vChild = vChildren[i];
/*jshint boss:true */
if (plugin=vChild.domNode.plugin) {
/*jshint boss:false */
keys = plugin.keys();
len2 = keys.length;
for (j=0; j<len2; j++) {
ns = keys[j];
vChild.domNode.unplug(ns);
}
}
processVChildNodes(vChild);
}
};
processVChildNodes(domElement.vnode);
},
/**
* Copies the attibute-values into element.model.
* Only processes the attributes that are defined through the Itag-class its `attrs`-property.
*
* @method attrsToModel
* @param domElement {HTMLElement} the itag that should be processed.
* @for itagCore
* @since 0.0.1
*/
attrsToModel: function(domElement) {
console.log(NAME+'attrsToModel');
var attrs = domElement._attrs,
attrValue, validValue;
attrs.each(function(value, key) {
attrValue = domElement.getAttr(key);
if (attrValue) {
switch (value.toLowerCase()) {
case 'boolean':
validValue = attrValue.validateBoolean();
attrValue = (attrValue==='true');
break;
case 'number':
validValue = attrValue.validateFloat();
attrValue = parseFloat(attrValue);
break;
case 'date':
validValue = attrValue.validateDate();
attrValue = attrValue.toDate();
break;
case 'string':
validValue = true;
break;
default:
validValue = false;
}
}
else if (value.toLowerCase()==='boolean') {
// undefined `boolean` attributes need to be stored as `false`
validValue = true;
attrValue = false;
}
else {
validValue = false;
}
validValue && domElement.defineWhenUndefined(key, attrValue);
});
},
/**
* Binds a model to the itag-element, making element.model equals the bound model.
* Immediately syncs the itag with the new model-data.
*
* Syncs the new vnode's childNodes with the dom.
*
* @method bindModel
* @param element {HTMLElement} element, which should be an Itag
* @param model {Object} the model to bind to the itag-element
* @param [mergeCurrent=false] {Boolean} when set true, current properties on the iTag's model that aren't defined
* in the new model, get merged into the new model.
* @since 0.0.1
*/
bindModel: function(element, model, mergeCurrent) {
console.log(NAME+'bindModel');
var instance = this,
observer;
if (element.isItag() && (element.model!==model) && !element.inside('.ce-design-node')) {
element.removeAttr('bound-model');
Object.protectedProp(element.vnode, 'ce_boundModel', true);
observer = element.getData('_observer');
element.model.unobserve(observer);
mergeCurrent && (model.merge(element.model, {full: true}));
element.model = model;
observer = function() {
itagCore.modelToAttrs(element);
element.syncUI();
};
element.model.observe(observer);
element.setData('_observer', observer);
if (!element.vnode.ce_initialized) {
instance.attrsToModel(element);
element.initUI(PROTO_SUPPORTED ? null : element.__proto__.constructor)
.renderUI();
}
element._afterBindModel();
element.syncUI();
element.itagRendered || instance.setRendered(element);
}
},
/**
* Retrieves modeldata set by the server inside the itag-element and binds this data into element.model
*
* @method extractModel
* @param domElement {HTMLElement} the itag that should be processed.
* @return {Object|null} the modeldata or null when not supplied
* @since 0.0.1
*/
extractModel: function(domElement) {
console.log(NAME+'extractModel');
var vnode = domElement.vnode,
vChildNodes = vnode.vChildNodes,
lastPos = vChildNodes.length - 1,
i = -1,
modelData, vChildNode, content;
// walk through the vChilds and handle the model-data:
while ((++i<lastPos) && (modelData===undefined)) {
vChildNode = vChildNodes[i];
if ((vChildNode.nodeType===8) && (vChildNode.text.startsWith('i-model:{'))) {
// modeldata was set
try {
content = vChildNode.text.replaceAll('<', '<').replaceAll('>', '>');
modelData = JSON.parseWithDate(content.substr(8));
}
catch(e) {
modelData = null;
console.warn(e);
}
vnode._removeChild(vChildNode);
}
}
return modelData || null;
},
/**
* Retrieves content set by the definition of the iTag. The content should be inside a comment-node
* inside the itag. The returnvalue is a container-node (DIV) where the content
* -as is specified by the comment-node- lies within as true HTML.
*
* @method extractContent
* @param domElement {HTMLElement} the itag that should be processed.
* @return {HTMLElement} a DIV-container with HTML inside
* @since 0.0.1
*/
extractContent: function(domElement, empty) {
console.log(NAME+'extractContent');
var vnode = domElement.vnode,
vChildNodes = vnode.vChildNodes,
lastPos = vChildNodes.length,
i = -1,
container = DOCUMENT.createElement('div'),
content, vChildNode;
// mark the container with a class -->
// so we know we don't need to render the itags anything inside:
container.setClass('ce-design-node');
// walk through the vChilds and handle the model-data:
while ((++i<lastPos) && !content) {
vChildNode = vChildNodes[i];
if ((vChildNode.nodeType===8) && (!vChildNode.text.startsWith('i-model:{'))) {
content = vChildNode.text.trim().replaceAll('<', '<').replaceAll('>', '>');
// to support nested comments (in case of nested iTags),
// we transform any text looking like --!> into -->
content = content.replaceAll('<!==', '<!--').replaceAll('==>', '-->');
container.vnode.setHTML(content, true);
empty || vnode._removeChild(vChildNode);
}
}
empty && domElement.empty();
return container;
},
/**
* Function that can be used ad the `filterFn` of event-listeners.
* Returns true for any HTML-element that is a rendered itag.
*
* @method itagFilter
* @param e {Object} the event-object passed by Event
* @return {Boolean} whether the HTML-element that is a rendered itag
* @since 0.0.1
*/
itagFilter: function(e) {
console.log(NAME+'itagFilter');
var node = e.target;
return node.vnode.isItag && node.getData('itagRendered');
},
/**
* Copies elemtn.model values into the attibute-values of the element.
* Only processes the attributes that are defined through the Itag-class its `attrs`-property.
*
* @method modelToAttrs
* @param domElement {HTMLElement} the itag that should be processed.
* @since 0.0.1
*/
modelToAttrs: function(domElement) {
console.log(NAME+'modelToAttrs');
var attrs = domElement._attrs,
model = domElement.model,
newAttrs = [];
attrs.each(function(value, key) {
model[key] && (newAttrs[newAttrs.length] = {name: key, value: model[key]});
});
(newAttrs.length>0) && domElement.setAttrs(newAttrs, true);
},
/**
* Searches through the dom for the specified itags and upgrades its HTMLElement.
*
* @method renderDomElements
* @param domElementConstructor {Class} the Class which belongs with the itag
* @since 0.0.1
*/
renderDomElements: function(domElementConstructor) {
console.log(NAME+'renderDomElements');
var itagName = domElementConstructor.$$itag,
pseudo = domElementConstructor.$$pseudo,
itagElements = pseudo ? DOCUMENT.getAll(itagName+'[is="'+pseudo+'"]') : DOCUMENT.getAll(itagName+':not([is])'),
len = itagElements.length,
i, itagElement;
for (i=0; i<len; i++) {
itagElement = itagElements[i];
this.upgradeElement(itagElement, domElementConstructor);
}
},
/**
* Defines the itag-element as being rendered.
*
* @method setRendered
* @param domElement {HTMLElement} the itag that should be processed.
* @since 0.0.1
*/
setRendered: function(domElement) {
console.log(NAME+'setRendered');
domElement.setClass(CLASS_ITAG_RENDERED, null, null, true);
domElement.setData('itagRendered', true);
domElement._itagReady || (domElement._itagReady=window.Promise.manage());
domElement._itagReady.fulfill();
},
/**
* Sets up all general itag-emitters.
*
* @method setupEmitters
* @since 0.0.1
*/
setupEmitters: function() {
console.log(NAME+'setupEmitters');
Event.after('*:'+NODE_CONTENT_CHANGE, function(e) {
var element = e.target;
/**
* Emitted when an itag changed its content
*
* @event *:change
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the dropzone
* @since 0.1
*/
element.emit('change', {model: element.model});
}, this.itagFilter);
},
setContentVisibility: function(ItagClass, value) {
console.log(NAME+'setContentVisibility');
(typeof value === 'boolean') && ItagClass.mergePrototypes({contentHidden: !value}, true, false, true);
},
/**
* Sets up all itag-watchers, giving itags its life behaviour.
*
* @method setupWatchers
* @since 0.0.1
*/
setupWatchers: function() {
console.log(NAME+'setupWatchers');
Event.after(
'UI:'+NODE_REMOVE,
function(e) {
var node = e.target;
node.destroyUI(PROTO_SUPPORTED ? null : node.__proto__.constructor);
},
itagCore.itagFilter
);
Event.before(
'*:manualfocus',
function(e) {
var node = e.target;
if (!node.isRendered()) {
e.halt();
node.itagReady().then(
function() {
// re-emit the focus
node.focus();
}
);
}
},
function(e) {
return e.target.vnode.isItag;
}
);
if (PROTO_SUPPORTED) {
Event.after(
'*:prototypechange',
function(e) {
var prototypes = e.prototypes,
ItagClass = e.target,
nodeList, node, i, length;
if ('init' in prototypes) {
nodeList = DOCUMENT.getAll(ItagClass.$$itag+'.'+CLASS_ITAG_RENDERED, true);
length = nodeList.length;
for (i=0; i<length; i++) {
node = nodeList[i];
node.reInitializeUI();
}
}
else if ('sync' in prototypes) {
nodeList = DOCUMENT.getAll(ItagClass.$$itag+'.'+CLASS_ITAG_RENDERED, true);
length = nodeList.length;
for (i=0; i<length; i++) {
node = nodeList[i];
node.syncUI();
}
}
},
function(e) {
return !!e.target.$$itag;
}
);
Event.after(
'*:prototyperemove',
function(e) {
var properties = e.properties,
ItagClass = e.target,
nodeList, node, i, length;
if (properties.contains('init')) {
nodeList = DOCUMENT.getAll(ItagClass.$$itag+'.'+CLASS_ITAG_RENDERED, true);
length = nodeList.length;
for (i=0; i<length; i++) {
node = nodeList[i];
node.reInitializeUI();
}
}
else if (properties.contains('sync')) {
nodeList = DOCUMENT.getAll(ItagClass.$$itag+'.'+CLASS_ITAG_RENDERED, true);
length = nodeList.length;
for (i=0; i<length; i++) {
node = nodeList[i];
node.syncUI();
}
}
},
function(e) {
return !!e.target.$$itag;
}
);
}
},
/**
* Upgrades the HTMLElement into an itag defined by domElementConstructor.
*
* @method upgradeElement
* @param domElement {HTMLElement} the itag that should be processed.
* @param domElementConstructor {Class} the Class which belongs with the itag
* @since 0.0.1
*/
upgradeElement: function(domElement, domElementConstructor) {
console.log(NAME+'upgradeElement');
var instance = this,
proto = domElementConstructor.prototype,
observer;
domElement.model || (domElement.model={});
if (!PROTO_SUPPORTED) {
mergeFlat(domElementConstructor, domElement);
domElement.__proto__ = proto;
domElement.__classCarier__ = domElementConstructor;
domElement.after(
'*:prototypechange',
function(e) {
var prototypes = e.prototypes;
mergeFlat(domElementConstructor, domElement);
if ('init' in prototypes) {
domElement.reInitializeUI(domElement.__proto__.constructor);
}
else if ('sync' in prototypes) {
domElement.syncUI();
}
},
function(e) {
return !!e.target.$$itag;
}
);
domElement.after(
'*:prototyperemove',
function(e) {
var properties = e.properties;
mergeFlat(domElementConstructor, domElement);
if (properties.contains('init')) {
domElement.reInitializeUI(domElement.__proto__.constructor);
}
else if (properties.contains('sync')) {
domElement.syncUI();
}
},
function(e) {
return !!e.target.$$itag;
}
);
}
else {
domElement.__proto__ = proto;
domElement.__classCarier__ = domElementConstructor;
}
// sync, but do this after the element is created:
// in the next eventcycle:
async(function(){
var needsToBind = (domElement.getAttr('bound-model')==='true');
// only if no modelbinding is needed, we can directly init, sync and make ready,
// otherwise we need to make this done by `bindModel`
BINDING_LIST.some(function(value, selector) {
domElement.matches(selector) && (needsToBind=true);
return needsToBind;
});
if (!needsToBind) {
instance.attrsToModel(domElement);
domElement.initUI(PROTO_SUPPORTED ? null : domElementConstructor)
.renderUI()
.syncUI();
instance.setRendered(domElement);
}
observer = function() {
instance.modelToAttrs(domElement);
domElement.syncUI();
};
domElement.model.observe(observer);
domElement.setData('_observer', observer);
});
}
};
/**
* Resets the focus on the right element inside an itag-instance after syncing.
* Only when a focusmanager is active and has focus.
*
* @method manageFocus
* @param domElement {Element} The itag to be inspected
* @private
* @since 0.0.1
*/
manageFocus = function(domElement) {
console.log(NAME+'manageFocus');
var focusManagerNode = domElement.getElement('.focussed[fm-manage]');
focusManagerNode && focusManagerNode.focus();
};
/**
* Reference to the original document.createElement.
*
* @method _createElement
* @param tag {String} tagname to be created
* @private
* @return {HTMLElement}
* @for document
* @since 0.0.1
*/
DOCUMENT._createElement = DOCUMENT.createElement;
/**
* Binds a model to the itag-element, making element.model equals the bound model.
* Immediately syncs the itag with the new model-data.
*
* Syncs the new vnode's childNodes with the dom.
*
* @method bindModel
* @param model {Object} the model to bind to the itag-element
* @param selector {String|HTMLElement} a css-selector or an HTMLElement where the data should be bound
* @param [mergeCurrent=false] {Boolean} when set true, current properties on the iTag that aren't defined
* in the new model, get merged into the new model.
* @param [fineGrain] {Function} A function that recieves `model` as argument and should return a
* manipulated (subset) of model as new model to be bound
* @return {Object} handler with a `detach()`-method which can be used to detach the binder
* @since 0.0.1
*/
DOCUMENT.bindModel = function(model, selector, mergeCurrent, fineGrain) {
console.log(NAME+'bindModel');
var documentElement = DOCUMENT.documentElement,
listener, elements, observer;
if ((typeof selector === 'string') && (selector.length>0) && !BINDING_LIST[selector]) {
BINDING_LIST[selector] = true;
elements = documentElement.getAll(selector);
elements.forEach(function(element) {
itagCore.bindModel(element, (typeof fineGrain==='function') ? fineGrain(element, model) : model, mergeCurrent);
});
listener = Event.after('UI:'+NODE_INSERT, function(e) {
var element = e.target;
itagCore.bindModel(element, (typeof fineGrain==='function') ? fineGrain(element, model) : model, mergeCurrent);
}, function(e) {
return e.target.matches(selector);
});
return {
detach: function() {
listener.detach();
elements = documentElement.getAll(selector);
elements.forEach(function(element) {
observer = element.getData('_observer');
element.model.unobserve(observer);
});
delete BINDING_LIST[selector];
}
};
}
// else
return {
detach: function() {
if (typeof selector === 'string') {
delete BINDING_LIST[selector];
}
}
};
};
/**
* Redefinition of document.createElement, enabling creation of itags.
*
* @method createElement
* @param tag {String} tagname to be created
* @param [suppressItagRender] {Boolean} to suppress Itags from rendering
* @return {HTMLElement}
* @since 0.0.1
*/
DOCUMENT.createElement = function(tag, suppressItagRender) {
console.log(NAME+'createElement '+tag);
var ItagClass = window.ITAGS[tag.toLowerCase()],
pos;
if (!suppressItagRender && ItagClass) {
return new ItagClass();
}
// we could run into a situation where we have an itag that is a pseudoclass
// yet suppressItagRender is `true`. This would lead into tagnames like: I-BUTTON#reset
// because native createElement cannot create these, we need to strip as from the #
else if (ItagClass && ((pos=tag.indexOf('#'))!==-1)) {
tag = tag.substr(0, pos);
}
return DOCUMENT._createElement(tag);
};
/**
* Internal hash containing all DOM-events that are listened for (at `document`).
*
*
* @property createItag
* @param itagName {String} The name of the itag-element, starting with `i-`
* @param [prototypes] {Object} Hash map of properties to be added to the prototype of the new class.
* @param [subClassable=true] {Boolean} whether the Class is subclassable. Can only be set to false on ItagClasses
* @type Class
* @for document
* @since 0.0.1
*/
Object.protectedProp(DOCUMENT, 'createItag', function(itagName, prototypes, subClassable) {
return Classes.ItagBaseClass.subClass.call(Classes.ItagBaseClass, itagName, prototypes, null, null, subClassable);
});
//===============================================================================
//== patching native prototypes =================================================
(function(FunctionPrototype) {
var originalSubClass = FunctionPrototype.subClass;
/**
* Backup of the original `mergePrototypes`-method.
*
* @method mergePrototypes
* @param prototypes {Object} Hash prototypes of properties to add to the prototype of this object
* @param force {Boolean} If true, existing members will be overwritten
* @private
* @chainable
* @since 0.0.1
*/
FunctionPrototype._mergePrototypes = FunctionPrototype.mergePrototypes;
/**
* Merges the given prototypes of properties into the `prototype` of the Class.
*
* **Note1 ** to be used on instances --> ONLY on Classes
* **Note2 ** properties with getters and/or unwritable will NOT be merged
*
* The members in the hash prototypes will become members with
* instances of the merged class.
*
* By default, this method will not override existing prototype members,
* unless the second argument `force` is true.
*
* In case of merging properties into an itag, a `*:prototypechange`-event gets emitted
*
* @method mergePrototypes
* @param prototypes {Object} Hash prototypes of properties to add to the prototype of this object
* @param [force=false] {Boolean} If true, existing members will be overwritten
* @param [silent=false] {Boolean} If true, no `*:prototypechange` event will get emitted
* @chainable
* @since 0.0.1
*/
FunctionPrototype.mergePrototypes = function(prototypes, force, silent) {
var instance = this,
overwriteProtected = arguments[3], // hidden private feature
itagEmitterName;
if (!instance.$$itag) {
// default mergePrototypes
instance._mergePrototypes(prototypes, force);
}
else {
instance._mergePrototypes(prototypes, force, ITAG_METHODS, overwriteProtected ? null : PROTECTED_MEMBERS);
/**
* Emitted when prototypes are set on an existing Itag-Class.
*
* @event *:prototypechange
* @param e {Object} eventobject including:
* @param e.prototypes {Object} Hash prototypes of properties to add to the prototype of this object
* @param e.force {Boolean} whether existing members are overwritten
* @since 0.1
*/
if (!silent) {
// cannot emit on the instance
itagEmitterName = instance.$$itag + (instance.$$pseudo ? '#'+instance.$$pseudo : '');
Event.emit(instance, itagEmitterName+':prototypechange', {prototypes: prototypes, force: !!force});
}
}
return instance;
};
/**
* Subclasses in Itag-Class into a pseudo-class: retaining its tagname, yet still subclassing.
* The pseudoclass gets identified by `i-parentclass#pseudo` and once rendered it has the signature of:
* <i-parentclass> is="pseudo" </i-parentclass>
*
* Syncs the new vnode's childNodes with the dom.
*
* @method pseudoClass
* @param pseudo {String} The pseudoname (without a minustoken), leading into the definition of `i-parent:pseudo`
* @param [prototypes] {Object} Hash map of properties to be added to the prototype of the new class.
* @param [chainInit=true] {Boolean} Whether -during instance creation- to automaticly construct in the complete hierarchy with the given constructor arguments.
* @param [chainDestroy=true] {Boolean} Whether -when the Element gets out if the DOM- to automaticly destroy in the complete hierarchy.
* @param [subClassable=true] {Boolean} whether the Class is subclassable. Can only be set to false on ItagClasses
* @return {Class}
* @since 0.0.1
*/
FunctionPrototype.pseudoClass = function(pseudo, prototypes, chainInit, chainDestroy, subClassable) {
var instance = this;
if (!instance.$$itag) {
console.warn(NAME, 'cannot pseudoClass '+pseudo+' for its Parent is no Itag-Class');
return instance;
}
if (typeof pseudo !== 'string') {
console.warn(NAME, 'cannot pseudoClass --> first argument needs to be s String');
return instance;
}
if (pseudo.contains('-')) {
console.warn(NAME, 'cannot pseudoClass '+pseudo+' --> name cannot consist a minus-token');
return instance;
}
return instance.subClass(instance.$$itag+'#'+pseudo , prototypes, chainInit, chainDestroy, subClassable);
};
/**
* Backup of the original `removePrototypes`-method.
*
* @method _removePrototypes
* @param properties
* @private
* @chainable
* @since 0.0.1
*/
FunctionPrototype._removePrototypes = FunctionPrototype.removePrototypes;
/**
* Redefines the childNodes of both the vnode as well as its related dom-node. The new
* definition replaces any previous nodes. (without touching unmodified nodes).
*
* Syncs the new vnode's childNodes with the dom.
*
* @method removePrototypes
* @param properties
* @chainable
* @since 0.0.1
*/
FunctionPrototype.removePrototypes = function(properties, silent) {
var instance = this,
itagEmitterName;
if (!instance.$$itag) {
// default mergePrototypes
instance._removePrototypes.apply(instance, arguments);
}
else {
instance._removePrototypes(properties, ITAG_METHODS);
/**
* Emitted when prototypes are removed off an existing Itag-Class.
*
* @event *:prototyperemove
* @param e {Object} eventobject including:
* @param e.prototypes {Object} Hash prototypes of properties to add to the prototype of this object
* @since 0.1
*/
if (!silent) {
// cannot emit on the instance
itagEmitterName = instance.$$itag + (instance.$$pseudo ? '#'+instance.$$pseudo : '');
Event.emit(instance, itagEmitterName+':prototyperemove', {properties: properties});
}
instance.prototype.emit('prototyperemove', {properties: properties});
}
return instance;
};
/**
* Backup of the original `setConstructor`-method.
*
* @method _setConstructor
* @param [constructorFn] {Function} The function that will serve as the new constructor for the class.
* If `undefined` defaults to `NOOP`
* @param [prototypes] {Object} Hash map of properties to be added to the prototype of the new class.
* @param [chainConstruct=true] {Boolean} Whether -during instance creation- to automaticly construct in the complete hierarchy with the given constructor arguments.
* @chainable
* @since 0.0.1
*/
FunctionPrototype._setConstructor = FunctionPrototype.setConstructor;
/**
* Redefines the constructor fo the Class
*
* @method setConstructor
* @param [constructorFn] {Function} The function that will serve as the new constructor for the class.
* If `undefined` defaults to `NOOP`
* @param [prototypes] {Object} Hash map of properties to be added to the prototype of the new class.
* @param [chainConstruct=true] {Boolean} Whether -during instance creation- to automaticly construct in the complete hierarchy with the given constructor arguments.
* @chainable
*/
FunctionPrototype.setConstructor = function(/* constructorFn, chainConstruct */) {
var instance = this;
if (instance.$$itag) {
console.warn(NAME, 'Itags don\t have a constructor --> you need to redefine "init()" by using mergePrototypes()');
return instance;
}
return instance._setConstructor.apply(instance, arguments);
};
/**
* Redefines the childNodes of both the vnode as well as its related dom-node. The new
* definition replaces any previous nodes. (without touching unmodified nodes).
*
* Syncs the new vnode's childNodes with the dom.
*
* @method subClass
* @param [constructorOrItagname] {Function|String} The function that will serve as constructor for the new class.
* If `undefined` defaults to `NOOP`
* When subClassing an ItagClass, a String should be passed as first argument
* @param [prototypes] {Object} Hash map of properties to be added to the prototype of the new class.
* @param [chainInit=true] {Boolean} Whether -during instance creation- to automaticly construct in the complete hierarchy with the given constructor arguments.
* @param [chainDestroy=true] {Boolean} Whether -when the Element gets out if the DOM- to automaticly destroy in the complete hierarchy.
* @param [subClassable=true] {Boolean} whether the Class is subclassable. Can only be set to false on ItagClasses
* @return {Class}
* @since 0.0.1
*/
FunctionPrototype.subClass = function(constructorOrItagname, prototypes, chainInit, chainDestroy, subClassable) {
var instance = this,
baseProt, proto, domElementConstructor, itagName, pseudo, registerName, itagNameSplit, itagEmitterName;
if (typeof constructorOrItagname === 'string') {
// Itag subclassing
if (typeof prototypes === 'boolean') {
subClassable = chainDestroy;
chainDestroy = chainInit;
chainInit = prototypes;
prototypes = null;
}
(typeof chainInit === 'boolean') || (chainInit=DEFAULT_CHAIN_INIT);
(typeof chainDestroy === 'boolean') || (chainDestroy=DEFAULT_CHAIN_DESTROY);
(typeof subClassable === 'boolean') || (subClassable=true);
itagName = constructorOrItagname.toLowerCase();
if (!itagName.startsWith('i-')) {
console.warn(NAME, 'invalid itagname '+itagName+' --> name should start with i-');
return instance;
}
if (window.ITAGS[itagName]) {
console.warn(itagName+' already exists: it will be redefined');
}
registerName = itagName;
itagNameSplit = itagName.split('#');
itagName = itagNameSplit[0];
pseudo = itagNameSplit[1]; // may be undefined
if (instance.$$itag && !instance.$$subClassable && !pseudo) {
console.warn(NAME, instance.$$itag+' cannot be sub-classed');
return instance;
}
// if instance.isItag, then we subclass an existing i-tag
baseProt = instance.prototype;
proto = Object.create(baseProt);
// merge some system function in case they don't exists
domElementConstructor = function() {
var domElement = DOCUMENT._createElement(itagName);
pseudo && domElement.vnode._setAttr('is', pseudo, true);
itagCore.upgradeElement(domElement, domElementConstructor);
return domElement;
};
domElementConstructor.prototype = proto;
// webkit doesn't let all objects to have their constructor redefined
// when directly assigned. Using `defineProperty will work:
Object.defineProperty(proto, 'constructor', {value: domElementConstructor});
domElementConstructor.$$itag = itagName;
domElementConstructor.$$pseudo = pseudo;
domElementConstructor.$$chainInited = chainInit ? true : false;
domElementConstructor.$$chainDestroyed = chainDestroy ? true : false;
domElementConstructor.$$super = baseProt;
domElementConstructor.$$orig = {};
domElementConstructor.$$subClassable = subClassable;
itagEmitterName = itagName + (pseudo ? '#'+pseudo : '');
domElementConstructor.mergePrototypes(Event.Emitter(itagEmitterName), true, true);
prototypes && domElementConstructor.mergePrototypes(prototypes, true, true);
// make emitting change-events unpreventable and unrenderable:
Event.defineEvent(itagEmitterName+':change').unPreventable();
Event.defineEvent(itagEmitterName+':prototypechange').unPreventable();
Event.defineEvent(itagEmitterName+':prototyperemove').unPreventable();
window.ITAGS[registerName] = domElementConstructor;
itagCore.renderDomElements(domElementConstructor);
return domElementConstructor;
}
else {
// Function subclassing
if (instance.$$itag) {
console.warn(NAME, 'subClassing '+instance.$$itag+' needs a "String" as first argument');
return instance;
}
return originalSubClass.apply(instance, arguments);
}
};
}(Function.prototype));
(function(ElementPrototype) {
var setAttributeBKP = ElementPrototype.setAttribute,
removeAttributeBKP = ElementPrototype.removeAttribute;
/**
* Binds a model to the itag-element, making element.model equals the bound model.
* Immediately syncs the itag with the new model-data.
*
* Syncs the new vnode's childNodes with the dom.
*
* @method bindModel
* @param model {Object} the model to bind to the itag-element
* @param [mergeCurrent=false] {Boolean} when set true, current properties on the iTag that aren't defined
* in the new model, get merged into the new model.
* @param [fineGrain] {Function} A function that recieves `model` as argument and should return a
* manipulated (subset) of model as new model to be bound
* @return {Object} handler with a `detach()`-method which can be used to detach the binder
* @since 0.0.1
*/
ElementPrototype.bindModel = function(model, mergeCurrent, fineGrain) {
var instance = this,
observer;
if (instance._syncUI) {
itagCore.bindModel(instance, (typeof fineGrain==='function') ? fineGrain(instance, model) : model, mergeCurrent);
return {
detach: function() {
observer = instance.getData('_observer');
instance.model.unobserve(observer);
}
};
}
// else for compatabilaty, return a detachFn
return {
detach: function() {}
};
};
/**
* Removes the attribute from the Element.
* In case of an Itag --> will remove the property of element.model
*
* Use removeAttr() to be able to chain.
*
* @method removeAttr
* @param attributeName {String}
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit
* @since 0.0.1
*/
ElementPrototype.removeAttribute = function(attributeName, silent) {
var instance = this;
if (!instance.isItag() || silent) {
removeAttributeBKP.apply(instance, arguments);
}
else {
if (instance._attrs[attributeName]) {
delete instance.model[attributeName];
}
else {
removeAttributeBKP.apply(instance, arguments);
}
}
};
/**
* Sets the attribute on the Element with the specified value.
* In case of an Itag --> will remove the property of element.model
*
* Alias for setAttr(), BUT differs in a way that setAttr is chainable, setAttribute is not.
*
* @method setAttribute
* @param attributeName {String}
* @param value {String} the value for the attributeName
* @param [silent=false] {Boolean} prevent node-mutation events by the Event-module to emit
* @since 0.0.1
*/
ElementPrototype.setAttribute = function(attributeName, value, silent) {
var instance = this,
valueType;
if (!instance.isItag() || silent) {
setAttributeBKP.apply(instance, arguments);
}
else {
/*jshint boss:true */
if (instance._attrs && (valueType=instance._attrs[attributeName])) {
/*jshint boss:false */
switch (valueType.toLowerCase()) {
case 'boolean':
value = (value==='true');
break;
case 'number':
value = parseFloat(value);
break;
case 'date':
value = value.toDate();
break;
}
instance.model[attributeName] = value;
}
else {
setAttributeBKP.apply(instance, arguments);
}
}
};
}(window.Element.prototype));
(function(HTMLElementPrototype) {
/**
* Flag that tells whether the HTMLElement is an Itag
*
* @method isItag
* @return {Boolean}
* @for HTMLElement
* @since 0.0.1
*/
HTMLElementPrototype.isItag = function() {
return this.vnode.isItag;
};
}(window.HTMLElement.prototype));
//===============================================================================
/**
* Creates the base ItagClass: the highest Class in the hierarchy of all ItagClasses.
* Will get extra properties merge into its prototype, which leads into the formation of `ItagBaseClass`.
*
* @method createItagBaseClass
* @protected
* @return {Class}
* @for itagCore
* @since 0.0.1
*/
var createItagBaseClass = function () {
return Function.prototype.subClass.apply(window.HTMLElement);
};
/**
* The base ItagClass: the highest Class in the hierarchy of all ItagClasses.
*
* @property ItagBaseClass
* @type Class
* @for Classes
* @since 0.0.1
*/
Object.protectedProp(Classes, 'ItagBaseClass', createItagBaseClass().mergePrototypes(EXTRA_BASE_MEMBERS, true, {}, {}));
// because `mergePrototypes` cannot merge object-getters, we will add the getter `$super` manually:
Object.defineProperties(Classes.ItagBaseClass.prototype, Classes.coreMethods);
/**
* Calculated value of the specified member at the parent-Class.
*
* @method $superProp
* @return {Any}
* @for ItagBaseClass
* @since 0.0.1
*/
Object.defineProperty(Classes.ItagBaseClass.prototype, '$superProp', {
value: function(/* member, *args */) {
var instance = this,
classCarierReturn = instance.__$superCarierStart__ || instance.__classCarier__ || instance.__methodClassCarier__,
currentClassCarier = instance.__classCarier__ || instance.__methodClassCarier__,
args = arguments,
superClass, superPrototype, firstArg, returnValue;
instance.__$superCarierStart__ = null;
if (args.length === 0) {
instance.__classCarier__ = classCarierReturn;
return;
}
superClass = currentClassCarier.$$super.constructor,
superPrototype = superClass.prototype,
firstArg = Array.prototype.shift.apply(args); // will decrease the length of args with one
firstArg = ITAG_METHODS[firstArg] || firstArg;
(firstArg === '_initUI') && (firstArg='initUI'); // to re-initiate chaining
(firstArg === '_destroyUI') && (firstArg='destroyUI'); // to re-initiate chaining
if ((firstArg==='initUI') && currentClassCarier.$$chainInited) {
console.warn('init cannot be invoked manually, because the Class is `chainInited`');
return currentClassCarier;
}
if ((firstArg==='destroyUI') && currentClassCarier.$$chainDestroyed) {
console.warn('destroy cannot be invoked manually, because the Class is `chainDestroyed`');
return currentClassCarier;
}
if (typeof superPrototype[firstArg] === 'function') {
instance.__classCarier__ = superClass;
if ((firstArg==='initUI') || (firstArg==='destroyUI')) {
returnValue = superPrototype[firstArg].call(instance, instance.__classCarier__);
}
else {
returnValue = superPrototype[firstArg].apply(instance, args);
}
}
instance.__classCarier__ = classCarierReturn;
return returnValue || superPrototype[firstArg];
}
});
itagCore.setupWatchers();
itagCore.setupEmitters();
Object.protectedProp(window, '_ItagCore', itagCore);
if (PROTOTYPE_CHAIN_CAN_BE_SET) {
/*
* Only for usage during testing --> can deactivate the usage of __proto__ making the itags
* upgraded my merging all ItagClass-members to the domElement-instance.
*
* @method setPrototypeChain
* @param activate
* @for itagCore
* @since 0.0.1
*/
itagCore.setPrototypeChain = function(activate) {
PROTO_SUPPORTED = activate ? !!Object.__proto__ : false;
};
}
return itagCore;
};
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./css/itags.core.css":5952,"itsa":2}],5954:[function(require,module,exports){
},{}],5955:[function(require,module,exports){
/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
* @license MIT
*/
var base64 = require('base64-js')
var ieee754 = require('ieee754')
exports.Buffer = Buffer
exports.SlowBuffer = Buffer
exports.INSPECT_MAX_BYTES = 50
Buffer.poolSize = 8192
/**
* If `TYPED_ARRAY_SUPPORT`:
* === true Use Uint8Array implementation (fastest)
* === false Use Object implementation (most compatible, even IE6)
*
* Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,
* Opera 11.6+, iOS 4.2+.
*
* Note:
*
* - Implementation must support adding new properties to `Uint8Array` instances.
* Firefox 4-29 lacked support, fixed in Firefox 30+.
* See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438.
*
* - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function.
*
* - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of
* incorrect length in some situations.
*
* We detect these buggy browsers and set `TYPED_ARRAY_SUPPORT` to `false` so they will
* get the Object implementation, which is slower but will work correctly.
*/
var TYPED_ARRAY_SUPPORT = (function () {
try {
var buf = new ArrayBuffer(0)
var arr = new Uint8Array(buf)
arr.foo = function () { return 42 }
return 42 === arr.foo() && // typed array instances can be augmented
typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray`
new Uint8Array(1).subarray(1, 1).byteLength === 0 // ie10 has broken `subarray`
} catch (e) {
return false
}
})()
/**
* Class: Buffer
* =============
*
* The Buffer constructor returns instances of `Uint8Array` that are augmented
* with function properties for all the node `Buffer` API functions. We use
* `Uint8Array` so that square bracket notation works as expected -- it returns
* a single octet.
*
* By augmenting the instances, we can avoid modifying the `Uint8Array`
* prototype.
*/
function Buffer (subject, encoding, noZero) {
if (!(this instanceof Buffer))
return new Buffer(subject, encoding, noZero)
var type = typeof subject
// Find the length
var length
if (type === 'number')
length = subject > 0 ? subject >>> 0 : 0
else if (type === 'string') {
if (encoding === 'base64')
subject = base64clean(subject)
length = Buffer.byteLength(subject, encoding)
} else if (type === 'object' && subject !== null) { // assume object is array-like
if (subject.type === 'Buffer' && isArray(subject.data))
subject = subject.data
length = +subject.length > 0 ? Math.floor(+subject.length) : 0
} else
throw new Error('First argument needs to be a number, array or string.')
var buf
if (TYPED_ARRAY_SUPPORT) {
// Preferred: Return an augmented `Uint8Array` instance for best performance
buf = Buffer._augment(new Uint8Array(length))
} else {
// Fallback: Return THIS instance of Buffer (created by `new`)
buf = this
buf.length = length
buf._isBuffer = true
}
var i
if (TYPED_ARRAY_SUPPORT && typeof subject.byteLength === 'number') {
// Speed optimization -- use set if we're copying from a typed array
buf._set(subject)
} else if (isArrayish(subject)) {
// Treat array-ish objects as a byte array
if (Buffer.isBuffer(subject)) {
for (i = 0; i < length; i++)
buf[i] = subject.readUInt8(i)
} else {
for (i = 0; i < length; i++)
buf[i] = ((subject[i] % 256) + 256) % 256
}
} else if (type === 'string') {
buf.write(subject, 0, encoding)
} else if (type === 'number' && !TYPED_ARRAY_SUPPORT && !noZero) {
for (i = 0; i < length; i++) {
buf[i] = 0
}
}
return buf
}
// STATIC METHODS
// ==============
Buffer.isEncoding = function (encoding) {
switch (String(encoding).toLowerCase()) {
case 'hex':
case 'utf8':
case 'utf-8':
case 'ascii':
case 'binary':
case 'base64':
case 'raw':
case 'ucs2':
case 'ucs-2':
case 'utf16le':
case 'utf-16le':
return true
default:
return false
}
}
Buffer.isBuffer = function (b) {
return !!(b != null && b._isBuffer)
}
Buffer.byteLength = function (str, encoding) {
var ret
str = str.toString()
switch (encoding || 'utf8') {
case 'hex':
ret = str.length / 2
break
case 'utf8':
case 'utf-8':
ret = utf8ToBytes(str).length
break
case 'ascii':
case 'binary':
case 'raw':
ret = str.length
break
case 'base64':
ret = base64ToBytes(str).length
break
case 'ucs2':
case 'ucs-2':
case 'utf16le':
case 'utf-16le':
ret = str.length * 2
break
default:
throw new Error('Unknown encoding')
}
return ret
}
Buffer.concat = function (list, totalLength) {
assert(isArray(list), 'Usage: Buffer.concat(list[, length])')
if (list.length === 0) {
return new Buffer(0)
} else if (list.length === 1) {
return list[0]
}
var i
if (totalLength === undefined) {
totalLength = 0
for (i = 0; i < list.length; i++) {
totalLength += list[i].length
}
}
var buf = new Buffer(totalLength)
var pos = 0
for (i = 0; i < list.length; i++) {
var item = list[i]
item.copy(buf, pos)
pos += item.length
}
return buf
}
Buffer.compare = function (a, b) {
assert(Buffer.isBuffer(a) && Buffer.isBuffer(b), 'Arguments must be Buffers')
var x = a.length
var y = b.length
for (var i = 0, len = Math.min(x, y); i < len && a[i] === b[i]; i++) {}
if (i !== len) {
x = a[i]
y = b[i]
}
if (x < y) {
return -1
}
if (y < x) {
return 1
}
return 0
}
// BUFFER INSTANCE METHODS
// =======================
function hexWrite (buf, string, offset, length) {
offset = Number(offset) || 0
var remaining = buf.length - offset
if (!length) {
length = remaining
} else {
length = Number(length)
if (length > remaining) {
length = remaining
}
}
// must be an even number of digits
var strLen = string.length
assert(strLen % 2 === 0, 'Invalid hex string')
if (length > strLen / 2) {
length = strLen / 2
}
for (var i = 0; i < length; i++) {
var byte = parseInt(string.substr(i * 2, 2), 16)
assert(!isNaN(byte), 'Invalid hex string')
buf[offset + i] = byte
}
return i
}
function utf8Write (buf, string, offset, length) {
var charsWritten = blitBuffer(utf8ToBytes(string), buf, offset, length)
return charsWritten
}
function asciiWrite (buf, string, offset, length) {
var charsWritten = blitBuffer(asciiToBytes(string), buf, offset, length)
return charsWritten
}
function binaryWrite (buf, string, offset, length) {
return asciiWrite(buf, string, offset, length)
}
function base64Write (buf, string, offset, length) {
var charsWritten = blitBuffer(base64ToBytes(string), buf, offset, length)
return charsWritten
}
function utf16leWrite (buf, string, offset, length) {
var charsWritten = blitBuffer(utf16leToBytes(string), buf, offset, length)
return charsWritten
}
Buffer.prototype.write = function (string, offset, length, encoding) {
// Support both (string, offset, length, encoding)
// and the legacy (string, encoding, offset, length)
if (isFinite(offset)) {
if (!isFinite(length)) {
encoding = length
length = undefined
}
} else { // legacy
var swap = encoding
encoding = offset
offset = length
length = swap
}
offset = Number(offset) || 0
var remaining = this.length - offset
if (!length) {
length = remaining
} else {
length = Number(length)
if (length > remaining) {
length = remaining
}
}
encoding = String(encoding || 'utf8').toLowerCase()
var ret
switch (encoding) {
case 'hex':
ret = hexWrite(this, string, offset, length)
break
case 'utf8':
case 'utf-8':
ret = utf8Write(this, string, offset, length)
break
case 'ascii':
ret = asciiWrite(this, string, offset, length)
break
case 'binary':
ret = binaryWrite(this, string, offset, length)
break
case 'base64':
ret = base64Write(this, string, offset, length)
break
case 'ucs2':
case 'ucs-2':
case 'utf16le':
case 'utf-16le':
ret = utf16leWrite(this, string, offset, length)
break
default:
throw new Error('Unknown encoding')
}
return ret
}
Buffer.prototype.toString = function (encoding, start, end) {
var self = this
encoding = String(encoding || 'utf8').toLowerCase()
start = Number(start) || 0
end = (end === undefined) ? self.length : Number(end)
// Fastpath empty strings
if (end === start)
return ''
var ret
switch (encoding) {
case 'hex':
ret = hexSlice(self, start, end)
break
case 'utf8':
case 'utf-8':
ret = utf8Slice(self, start, end)
break
case 'ascii':
ret = asciiSlice(self, start, end)
break
case 'binary':
ret = binarySlice(self, start, end)
break
case 'base64':
ret = base64Slice(self, start, end)
break
case 'ucs2':
case 'ucs-2':
case 'utf16le':
case 'utf-16le':
ret = utf16leSlice(self, start, end)
break
default:
throw new Error('Unknown encoding')
}
return ret
}
Buffer.prototype.toJSON = function () {
return {
type: 'Buffer',
data: Array.prototype.slice.call(this._arr || this, 0)
}
}
Buffer.prototype.equals = function (b) {
assert(Buffer.isBuffer(b), 'Argument must be a Buffer')
return Buffer.compare(this, b) === 0
}
Buffer.prototype.compare = function (b) {
assert(Buffer.isBuffer(b), 'Argument must be a Buffer')
return Buffer.compare(this, b)
}
// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
Buffer.prototype.copy = function (target, target_start, start, end) {
var source = this
if (!start) start = 0
if (!end && end !== 0) end = this.length
if (!target_start) target_start = 0
// Copy 0 bytes; we're done
if (end === start) return
if (target.length === 0 || source.length === 0) return
// Fatal error conditions
assert(end >= start, 'sourceEnd < sourceStart')
assert(target_start >= 0 && target_start < target.length,
'targetStart out of bounds')
assert(start >= 0 && start < source.length, 'sourceStart out of bounds')
assert(end >= 0 && end <= source.length, 'sourceEnd out of bounds')
// Are we oob?
if (end > this.length)
end = this.length
if (target.length - target_start < end - start)
end = target.length - target_start + start
var len = end - start
if (len < 100 || !TYPED_ARRAY_SUPPORT) {
for (var i = 0; i < len; i++) {
target[i + target_start] = this[i + start]
}
} else {
target._set(this.subarray(start, start + len), target_start)
}
}
function base64Slice (buf, start, end) {
if (start === 0 && end === buf.length) {
return base64.fromByteArray(buf)
} else {
return base64.fromByteArray(buf.slice(start, end))
}
}
function utf8Slice (buf, start, end) {
var res = ''
var tmp = ''
end = Math.min(buf.length, end)
for (var i = start; i < end; i++) {
if (buf[i] <= 0x7F) {
res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i])
tmp = ''
} else {
tmp += '%' + buf[i].toString(16)
}
}
return res + decodeUtf8Char(tmp)
}
function asciiSlice (buf, start, end) {
var ret = ''
end = Math.min(buf.length, end)
for (var i = start; i < end; i++) {
ret += String.fromCharCode(buf[i])
}
return ret
}
function binarySlice (buf, start, end) {
return asciiSlice(buf, start, end)
}
function hexSlice (buf, start, end) {
var len = buf.length
if (!start || start < 0) start = 0
if (!end || end < 0 || end > len) end = len
var out = ''
for (var i = start; i < end; i++) {
out += toHex(buf[i])
}
return out
}
function utf16leSlice (buf, start, end) {
var bytes = buf.slice(start, end)
var res = ''
for (var i = 0; i < bytes.length; i += 2) {
res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256)
}
return res
}
Buffer.prototype.slice = function (start, end) {
var len = this.length
start = ~~start
end = end === undefined ? len : ~~end
if (start < 0) {
start += len;
if (start < 0)
start = 0
} else if (start > len) {
start = len
}
if (end < 0) {
end += len
if (end < 0)
end = 0
} else if (end > len) {
end = len
}
if (end < start)
end = start
if (TYPED_ARRAY_SUPPORT) {
return Buffer._augment(this.subarray(start, end))
} else {
var sliceLen = end - start
var newBuf = new Buffer(sliceLen, undefined, true)
for (var i = 0; i < sliceLen; i++) {
newBuf[i] = this[i + start]
}
return newBuf
}
}
// `get` will be removed in Node 0.13+
Buffer.prototype.get = function (offset) {
console.log('.get() is deprecated. Access using array indexes instead.')
return this.readUInt8(offset)
}
// `set` will be removed in Node 0.13+
Buffer.prototype.set = function (v, offset) {
console.log('.set() is deprecated. Access using array indexes instead.')
return this.writeUInt8(v, offset)
}
Buffer.prototype.readUInt8 = function (offset, noAssert) {
if (!noAssert) {
assert(offset !== undefined && offset !== null, 'missing offset')
assert(offset < this.length, 'Trying to read beyond buffer length')
}
if (offset >= this.length)
return
return this[offset]
}
function readUInt16 (buf, offset, littleEndian, noAssert) {
if (!noAssert) {
assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
assert(offset !== undefined && offset !== null, 'missing offset')
assert(offset + 1 < buf.length, 'Trying to read beyond buffer length')
}
var len = buf.length
if (offset >= len)
return
var val
if (littleEndian) {
val = buf[offset]
if (offset + 1 < len)
val |= buf[offset + 1] << 8
} else {
val = buf[offset] << 8
if (offset + 1 < len)
val |= buf[offset + 1]
}
return val
}
Buffer.prototype.readUInt16LE = function (offset, noAssert) {
return readUInt16(this, offset, true, noAssert)
}
Buffer.prototype.readUInt16BE = function (offset, noAssert) {
return readUInt16(this, offset, false, noAssert)
}
function readUInt32 (buf, offset, littleEndian, noAssert) {
if (!noAssert) {
assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
assert(offset !== undefined && offset !== null, 'missing offset')
assert(offset + 3 < buf.length, 'Trying to read beyond buffer length')
}
var len = buf.length
if (offset >= len)
return
var val
if (littleEndian) {
if (offset + 2 < len)
val = buf[offset + 2] << 16
if (offset + 1 < len)
val |= buf[offset + 1] << 8
val |= buf[offset]
if (offset + 3 < len)
val = val + (buf[offset + 3] << 24 >>> 0)
} else {
if (offset + 1 < len)
val = buf[offset + 1] << 16
if (offset + 2 < len)
val |= buf[offset + 2] << 8
if (offset + 3 < len)
val |= buf[offset + 3]
val = val + (buf[offset] << 24 >>> 0)
}
return val
}
Buffer.prototype.readUInt32LE = function (offset, noAssert) {
return readUInt32(this, offset, true, noAssert)
}
Buffer.prototype.readUInt32BE = function (offset, noAssert) {
return readUInt32(this, offset, false, noAssert)
}
Buffer.prototype.readInt8 = function (offset, noAssert) {
if (!noAssert) {
assert(offset !== undefined && offset !== null,
'missing offset')
assert(offset < this.length, 'Trying to read beyond buffer length')
}
if (offset >= this.length)
return
var neg = this[offset] & 0x80
if (neg)
return (0xff - this[offset] + 1) * -1
else
return this[offset]
}
function readInt16 (buf, offset, littleEndian, noAssert) {
if (!noAssert) {
assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
assert(offset !== undefined && offset !== null, 'missing offset')
assert(offset + 1 < buf.length, 'Trying to read beyond buffer length')
}
var len = buf.length
if (offset >= len)
return
var val = readUInt16(buf, offset, littleEndian, true)
var neg = val & 0x8000
if (neg)
return (0xffff - val + 1) * -1
else
return val
}
Buffer.prototype.readInt16LE = function (offset, noAssert) {
return readInt16(this, offset, true, noAssert)
}
Buffer.prototype.readInt16BE = function (offset, noAssert) {
return readInt16(this, offset, false, noAssert)
}
function readInt32 (buf, offset, littleEndian, noAssert) {
if (!noAssert) {
assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
assert(offset !== undefined && offset !== null, 'missing offset')
assert(offset + 3 < buf.length, 'Trying to read beyond buffer length')
}
var len = buf.length
if (offset >= len)
return
var val = readUInt32(buf, offset, littleEndian, true)
var neg = val & 0x80000000
if (neg)
return (0xffffffff - val + 1) * -1
else
return val
}
Buffer.prototype.readInt32LE = function (offset, noAssert) {
return readInt32(this, offset, true, noAssert)
}
Buffer.prototype.readInt32BE = function (offset, noAssert) {
return readInt32(this, offset, false, noAssert)
}
function readFloat (buf, offset, littleEndian, noAssert) {
if (!noAssert) {
assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
assert(offset + 3 < buf.length, 'Trying to read beyond buffer length')
}
return ieee754.read(buf, offset, littleEndian, 23, 4)
}
Buffer.prototype.readFloatLE = function (offset, noAssert) {
return readFloat(this, offset, true, noAssert)
}
Buffer.prototype.readFloatBE = function (offset, noAssert) {
return readFloat(this, offset, false, noAssert)
}
function readDouble (buf, offset, littleEndian, noAssert) {
if (!noAssert) {
assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
assert(offset + 7 < buf.length, 'Trying to read beyond buffer length')
}
return ieee754.read(buf, offset, littleEndian, 52, 8)
}
Buffer.prototype.readDoubleLE = function (offset, noAssert) {
return readDouble(this, offset, true, noAssert)
}
Buffer.prototype.readDoubleBE = function (offset, noAssert) {
return readDouble(this, offset, false, noAssert)
}
Buffer.prototype.writeUInt8 = function (value, offset, noAssert) {
if (!noAssert) {
assert(value !== undefined && value !== null, 'missing value')
assert(offset !== undefined && offset !== null, 'missing offset')
assert(offset < this.length, 'trying to write beyond buffer length')
verifuint(value, 0xff)
}
if (offset >= this.length) return
this[offset] = value
return offset + 1
}
function writeUInt16 (buf, value, offset, littleEndian, noAssert) {
if (!noAssert) {
assert(value !== undefined && value !== null, 'missing value')
assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
assert(offset !== undefined && offset !== null, 'missing offset')
assert(offset + 1 < buf.length, 'trying to write beyond buffer length')
verifuint(value, 0xffff)
}
var len = buf.length
if (offset >= len)
return
for (var i = 0, j = Math.min(len - offset, 2); i < j; i++) {
buf[offset + i] =
(value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>>
(littleEndian ? i : 1 - i) * 8
}
return offset + 2
}
Buffer.prototype.writeUInt16LE = function (value, offset, noAssert) {
return writeUInt16(this, value, offset, true, noAssert)
}
Buffer.prototype.writeUInt16BE = function (value, offset, noAssert) {
return writeUInt16(this, value, offset, false, noAssert)
}
function writeUInt32 (buf, value, offset, littleEndian, noAssert) {
if (!noAssert) {
assert(value !== undefined && value !== null, 'missing value')
assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
assert(offset !== undefined && offset !== null, 'missing offset')
assert(offset + 3 < buf.length, 'trying to write beyond buffer length')
verifuint(value, 0xffffffff)
}
var len = buf.length
if (offset >= len)
return
for (var i = 0, j = Math.min(len - offset, 4); i < j; i++) {
buf[offset + i] =
(value >>> (littleEndian ? i : 3 - i) * 8) & 0xff
}
return offset + 4
}
Buffer.prototype.writeUInt32LE = function (value, offset, noAssert) {
return writeUInt32(this, value, offset, true, noAssert)
}
Buffer.prototype.writeUInt32BE = function (value, offset, noAssert) {
return writeUInt32(this, value, offset, false, noAssert)
}
Buffer.prototype.writeInt8 = function (value, offset, noAssert) {
if (!noAssert) {
assert(value !== undefined && value !== null, 'missing value')
assert(offset !== undefined && offset !== null, 'missing offset')
assert(offset < this.length, 'Trying to write beyond buffer length')
verifsint(value, 0x7f, -0x80)
}
if (offset >= this.length)
return
if (value >= 0)
this.writeUInt8(value, offset, noAssert)
else
this.writeUInt8(0xff + value + 1, offset, noAssert)
return offset + 1
}
function writeInt16 (buf, value, offset, littleEndian, noAssert) {
if (!noAssert) {
assert(value !== undefined && value !== null, 'missing value')
assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
assert(offset !== undefined && offset !== null, 'missing offset')
assert(offset + 1 < buf.length, 'Trying to write beyond buffer length')
verifsint(value, 0x7fff, -0x8000)
}
var len = buf.length
if (offset >= len)
return
if (value >= 0)
writeUInt16(buf, value, offset, littleEndian, noAssert)
else
writeUInt16(buf, 0xffff + value + 1, offset, littleEndian, noAssert)
return offset + 2
}
Buffer.prototype.writeInt16LE = function (value, offset, noAssert) {
return writeInt16(this, value, offset, true, noAssert)
}
Buffer.prototype.writeInt16BE = function (value, offset, noAssert) {
return writeInt16(this, value, offset, false, noAssert)
}
function writeInt32 (buf, value, offset, littleEndian, noAssert) {
if (!noAssert) {
assert(value !== undefined && value !== null, 'missing value')
assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
assert(offset !== undefined && offset !== null, 'missing offset')
assert(offset + 3 < buf.length, 'Trying to write beyond buffer length')
verifsint(value, 0x7fffffff, -0x80000000)
}
var len = buf.length
if (offset >= len)
return
if (value >= 0)
writeUInt32(buf, value, offset, littleEndian, noAssert)
else
writeUInt32(buf, 0xffffffff + value + 1, offset, littleEndian, noAssert)
return offset + 4
}
Buffer.prototype.writeInt32LE = function (value, offset, noAssert) {
return writeInt32(this, value, offset, true, noAssert)
}
Buffer.prototype.writeInt32BE = function (value, offset, noAssert) {
return writeInt32(this, value, offset, false, noAssert)
}
function writeFloat (buf, value, offset, littleEndian, noAssert) {
if (!noAssert) {
assert(value !== undefined && value !== null, 'missing value')
assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
assert(offset !== undefined && offset !== null, 'missing offset')
assert(offset + 3 < buf.length, 'Trying to write beyond buffer length')
verifIEEE754(value, 3.4028234663852886e+38, -3.4028234663852886e+38)
}
var len = buf.length
if (offset >= len)
return
ieee754.write(buf, value, offset, littleEndian, 23, 4)
return offset + 4
}
Buffer.prototype.writeFloatLE = function (value, offset, noAssert) {
return writeFloat(this, value, offset, true, noAssert)
}
Buffer.prototype.writeFloatBE = function (value, offset, noAssert) {
return writeFloat(this, value, offset, false, noAssert)
}
function writeDouble (buf, value, offset, littleEndian, noAssert) {
if (!noAssert) {
assert(value !== undefined && value !== null, 'missing value')
assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
assert(offset !== undefined && offset !== null, 'missing offset')
assert(offset + 7 < buf.length,
'Trying to write beyond buffer length')
verifIEEE754(value, 1.7976931348623157E+308, -1.7976931348623157E+308)
}
var len = buf.length
if (offset >= len)
return
ieee754.write(buf, value, offset, littleEndian, 52, 8)
return offset + 8
}
Buffer.prototype.writeDoubleLE = function (value, offset, noAssert) {
return writeDouble(this, value, offset, true, noAssert)
}
Buffer.prototype.writeDoubleBE = function (value, offset, noAssert) {
return writeDouble(this, value, offset, false, noAssert)
}
// fill(value, start=0, end=buffer.length)
Buffer.prototype.fill = function (value, start, end) {
if (!value) value = 0
if (!start) start = 0
if (!end) end = this.length
assert(end >= start, 'end < start')
// Fill 0 bytes; we're done
if (end === start) return
if (this.length === 0) return
assert(start >= 0 && start < this.length, 'start out of bounds')
assert(end >= 0 && end <= this.length, 'end out of bounds')
var i
if (typeof value === 'number') {
for (i = start; i < end; i++) {
this[i] = value
}
} else {
var bytes = utf8ToBytes(value.toString())
var len = bytes.length
for (i = start; i < end; i++) {
this[i] = bytes[i % len]
}
}
return this
}
Buffer.prototype.inspect = function () {
var out = []
var len = this.length
for (var i = 0; i < len; i++) {
out[i] = toHex(this[i])
if (i === exports.INSPECT_MAX_BYTES) {
out[i + 1] = '...'
break
}
}
return '<Buffer ' + out.join(' ') + '>'
}
/**
* Creates a new `ArrayBuffer` with the *copied* memory of the buffer instance.
* Added in Node 0.12. Only available in browsers that support ArrayBuffer.
*/
Buffer.prototype.toArrayBuffer = function () {
if (typeof Uint8Array !== 'undefined') {
if (TYPED_ARRAY_SUPPORT) {
return (new Buffer(this)).buffer
} else {
var buf = new Uint8Array(this.length)
for (var i = 0, len = buf.length; i < len; i += 1) {
buf[i] = this[i]
}
return buf.buffer
}
} else {
throw new Error('Buffer.toArrayBuffer not supported in this browser')
}
}
// HELPER FUNCTIONS
// ================
var BP = Buffer.prototype
/**
* Augment a Uint8Array *instance* (not the Uint8Array class!) with Buffer methods
*/
Buffer._augment = function (arr) {
arr._isBuffer = true
// save reference to original Uint8Array get/set methods before overwriting
arr._get = arr.get
arr._set = arr.set
// deprecated, will be removed in node 0.13+
arr.get = BP.get
arr.set = BP.set
arr.write = BP.write
arr.toString = BP.toString
arr.toLocaleString = BP.toString
arr.toJSON = BP.toJSON
arr.equals = BP.equals
arr.compare = BP.compare
arr.copy = BP.copy
arr.slice = BP.slice
arr.readUInt8 = BP.readUInt8
arr.readUInt16LE = BP.readUInt16LE
arr.readUInt16BE = BP.readUInt16BE
arr.readUInt32LE = BP.readUInt32LE
arr.readUInt32BE = BP.readUInt32BE
arr.readInt8 = BP.readInt8
arr.readInt16LE = BP.readInt16LE
arr.readInt16BE = BP.readInt16BE
arr.readInt32LE = BP.readInt32LE
arr.readInt32BE = BP.readInt32BE
arr.readFloatLE = BP.readFloatLE
arr.readFloatBE = BP.readFloatBE
arr.readDoubleLE = BP.readDoubleLE
arr.readDoubleBE = BP.readDoubleBE
arr.writeUInt8 = BP.writeUInt8
arr.writeUInt16LE = BP.writeUInt16LE
arr.writeUInt16BE = BP.writeUInt16BE
arr.writeUInt32LE = BP.writeUInt32LE
arr.writeUInt32BE = BP.writeUInt32BE
arr.writeInt8 = BP.writeInt8
arr.writeInt16LE = BP.writeInt16LE
arr.writeInt16BE = BP.writeInt16BE
arr.writeInt32LE = BP.writeInt32LE
arr.writeInt32BE = BP.writeInt32BE
arr.writeFloatLE = BP.writeFloatLE
arr.writeFloatBE = BP.writeFloatBE
arr.writeDoubleLE = BP.writeDoubleLE
arr.writeDoubleBE = BP.writeDoubleBE
arr.fill = BP.fill
arr.inspect = BP.inspect
arr.toArrayBuffer = BP.toArrayBuffer
return arr
}
var INVALID_BASE64_RE = /[^+\/0-9A-z]/g
function base64clean (str) {
// Node strips out invalid characters like \n and \t from the string, base64-js does not
str = stringtrim(str).replace(INVALID_BASE64_RE, '')
// Node allows for non-padded base64 strings (missing trailing ===), base64-js does not
while (str.length % 4 !== 0) {
str = str + '='
}
return str
}
function stringtrim (str) {
if (str.trim) return str.trim()
return str.replace(/^\s+|\s+$/g, '')
}
function isArray (subject) {
return (Array.isArray || function (subject) {
return Object.prototype.toString.call(subject) === '[object Array]'
})(subject)
}
function isArrayish (subject) {
return isArray(subject) || Buffer.isBuffer(subject) ||
subject && typeof subject === 'object' &&
typeof subject.length === 'number'
}
function toHex (n) {
if (n < 16) return '0' + n.toString(16)
return n.toString(16)
}
function utf8ToBytes (str) {
var byteArray = []
for (var i = 0; i < str.length; i++) {
var b = str.charCodeAt(i)
if (b <= 0x7F) {
byteArray.push(b)
} else {
var start = i
if (b >= 0xD800 && b <= 0xDFFF) i++
var h = encodeURIComponent(str.slice(start, i+1)).substr(1).split('%')
for (var j = 0; j < h.length; j++) {
byteArray.push(parseInt(h[j], 16))
}
}
}
return byteArray
}
function asciiToBytes (str) {
var byteArray = []
for (var i = 0; i < str.length; i++) {
// Node's code seems to be doing this and not & 0x7F..
byteArray.push(str.charCodeAt(i) & 0xFF)
}
return byteArray
}
function utf16leToBytes (str) {
var c, hi, lo
var byteArray = []
for (var i = 0; i < str.length; i++) {
c = str.charCodeAt(i)
hi = c >> 8
lo = c % 256
byteArray.push(lo)
byteArray.push(hi)
}
return byteArray
}
function base64ToBytes (str) {
return base64.toByteArray(str)
}
function blitBuffer (src, dst, offset, length) {
for (var i = 0; i < length; i++) {
if ((i + offset >= dst.length) || (i >= src.length))
break
dst[i + offset] = src[i]
}
return i
}
function decodeUtf8Char (str) {
try {
return decodeURIComponent(str)
} catch (err) {
return String.fromCharCode(0xFFFD) // UTF 8 invalid char
}
}
/*
* We have to make sure that the value is a valid integer. This means that it
* is non-negative. It has no fractional component and that it does not
* exceed the maximum allowed value.
*/
function verifuint (value, max) {
assert(typeof value === 'number', 'cannot write a non-number as a number')
assert(value >= 0, 'specified a negative value for writing an unsigned value')
assert(value <= max, 'value is larger than maximum value for type')
assert(Math.floor(value) === value, 'value has a fractional component')
}
function verifsint (value, max, min) {
assert(typeof value === 'number', 'cannot write a non-number as a number')
assert(value <= max, 'value larger than maximum allowed value')
assert(value >= min, 'value smaller than minimum allowed value')
assert(Math.floor(value) === value, 'value has a fractional component')
}
function verifIEEE754 (value, max, min) {
assert(typeof value === 'number', 'cannot write a non-number as a number')
assert(value <= max, 'value larger than maximum allowed value')
assert(value >= min, 'value smaller than minimum allowed value')
}
function assert (test, message) {
if (!test) throw new Error(message || 'Failed assertion')
}
},{"base64-js":5956,"ieee754":5957}],5956:[function(require,module,exports){
var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
;(function (exports) {
'use strict';
var Arr = (typeof Uint8Array !== 'undefined')
? Uint8Array
: Array
var PLUS = '+'.charCodeAt(0)
var SLASH = '/'.charCodeAt(0)
var NUMBER = '0'.charCodeAt(0)
var LOWER = 'a'.charCodeAt(0)
var UPPER = 'A'.charCodeAt(0)
function decode (elt) {
var code = elt.charCodeAt(0)
if (code === PLUS)
return 62 // '+'
if (code === SLASH)
return 63 // '/'
if (code < NUMBER)
return -1 //no match
if (code < NUMBER + 10)
return code - NUMBER + 26 + 26
if (code < UPPER + 26)
return code - UPPER
if (code < LOWER + 26)
return code - LOWER + 26
}
function b64ToByteArray (b64) {
var i, j, l, tmp, placeHolders, arr
if (b64.length % 4 > 0) {
throw new Error('Invalid string. Length must be a multiple of 4')
}
// the number of equal signs (place holders)
// if there are two placeholders, than the two characters before it
// represent one byte
// if there is only one, then the three characters before it represent 2 bytes
// this is just a cheap hack to not do indexOf twice
var len = b64.length
placeHolders = '=' === b64.charAt(len - 2) ? 2 : '=' === b64.charAt(len - 1) ? 1 : 0
// base64 is 4/3 + up to two characters of the original data
arr = new Arr(b64.length * 3 / 4 - placeHolders)
// if there are placeholders, only get up to the last complete 4 chars
l = placeHolders > 0 ? b64.length - 4 : b64.length
var L = 0
function push (v) {
arr[L++] = v
}
for (i = 0, j = 0; i < l; i += 4, j += 3) {
tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3))
push((tmp & 0xFF0000) >> 16)
push((tmp & 0xFF00) >> 8)
push(tmp & 0xFF)
}
if (placeHolders === 2) {
tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4)
push(tmp & 0xFF)
} else if (placeHolders === 1) {
tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2)
push((tmp >> 8) & 0xFF)
push(tmp & 0xFF)
}
return arr
}
function uint8ToBase64 (uint8) {
var i,
extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes
output = "",
temp, length
function encode (num) {
return lookup.charAt(num)
}
function tripletToBase64 (num) {
return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F)
}
// go through the array every three bytes, we'll deal with trailing stuff later
for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) {
temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2])
output += tripletToBase64(temp)
}
// pad the end with zeros, but make sure to not forget the extra bytes
switch (extraBytes) {
case 1:
temp = uint8[uint8.length - 1]
output += encode(temp >> 2)
output += encode((temp << 4) & 0x3F)
output += '=='
break
case 2:
temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1])
output += encode(temp >> 10)
output += encode((temp >> 4) & 0x3F)
output += encode((temp << 2) & 0x3F)
output += '='
break
}
return output
}
exports.toByteArray = b64ToByteArray
exports.fromByteArray = uint8ToBase64
}(typeof exports === 'undefined' ? (this.base64js = {}) : exports))
},{}],5957:[function(require,module,exports){
exports.read = function(buffer, offset, isLE, mLen, nBytes) {
var e, m,
eLen = nBytes * 8 - mLen - 1,
eMax = (1 << eLen) - 1,
eBias = eMax >> 1,
nBits = -7,
i = isLE ? (nBytes - 1) : 0,
d = isLE ? -1 : 1,
s = buffer[offset + i];
i += d;
e = s & ((1 << (-nBits)) - 1);
s >>= (-nBits);
nBits += eLen;
for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8);
m = e & ((1 << (-nBits)) - 1);
e >>= (-nBits);
nBits += mLen;
for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8);
if (e === 0) {
e = 1 - eBias;
} else if (e === eMax) {
return m ? NaN : ((s ? -1 : 1) * Infinity);
} else {
m = m + Math.pow(2, mLen);
e = e - eBias;
}
return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
};
exports.write = function(buffer, value, offset, isLE, mLen, nBytes) {
var e, m, c,
eLen = nBytes * 8 - mLen - 1,
eMax = (1 << eLen) - 1,
eBias = eMax >> 1,
rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0),
i = isLE ? 0 : (nBytes - 1),
d = isLE ? 1 : -1,
s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0;
value = Math.abs(value);
if (isNaN(value) || value === Infinity) {
m = isNaN(value) ? 1 : 0;
e = eMax;
} else {
e = Math.floor(Math.log(value) / Math.LN2);
if (value * (c = Math.pow(2, -e)) < 1) {
e--;
c *= 2;
}
if (e + eBias >= 1) {
value += rt / c;
} else {
value += rt * Math.pow(2, 1 - eBias);
}
if (value * c >= 2) {
e++;
c /= 2;
}
if (e + eBias >= eMax) {
m = 0;
e = eMax;
} else if (e + eBias >= 1) {
m = (value * c - 1) * Math.pow(2, mLen);
e = e + eBias;
} else {
m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
e = 0;
}
}
for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8);
e = (e << mLen) | m;
eLen += mLen;
for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8);
buffer[offset + i - d] |= s * 128;
};
},{}],5958:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
function EventEmitter() {
this._events = this._events || {};
this._maxListeners = this._maxListeners || undefined;
}
module.exports = EventEmitter;
// Backwards-compat with node 0.10.x
EventEmitter.EventEmitter = EventEmitter;
EventEmitter.prototype._events = undefined;
EventEmitter.prototype._maxListeners = undefined;
// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
EventEmitter.defaultMaxListeners = 10;
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function(n) {
if (!isNumber(n) || n < 0 || isNaN(n))
throw TypeError('n must be a positive number');
this._maxListeners = n;
return this;
};
EventEmitter.prototype.emit = function(type) {
var er, handler, len, args, i, listeners;
if (!this._events)
this._events = {};
// If there is no 'error' event listener then throw.
if (type === 'error') {
if (!this._events.error ||
(isObject(this._events.error) && !this._events.error.length)) {
er = arguments[1];
if (er instanceof Error) {
throw er; // Unhandled 'error' event
} else {
throw TypeError('Uncaught, unspecified "error" event.');
}
return false;
}
}
handler = this._events[type];
if (isUndefined(handler))
return false;
if (isFunction(handler)) {
switch (arguments.length) {
// fast cases
case 1:
handler.call(this);
break;
case 2:
handler.call(this, arguments[1]);
break;
case 3:
handler.call(this, arguments[1], arguments[2]);
break;
// slower
default:
len = arguments.length;
args = new Array(len - 1);
for (i = 1; i < len; i++)
args[i - 1] = arguments[i];
handler.apply(this, args);
}
} else if (isObject(handler)) {
len = arguments.length;
args = new Array(len - 1);
for (i = 1; i < len; i++)
args[i - 1] = arguments[i];
listeners = handler.slice();
len = listeners.length;
for (i = 0; i < len; i++)
listeners[i].apply(this, args);
}
return true;
};
EventEmitter.prototype.addListener = function(type, listener) {
var m;
if (!isFunction(listener))
throw TypeError('listener must be a function');
if (!this._events)
this._events = {};
// To avoid recursion in the case that type === "newListener"! Before
// adding it to the listeners, first emit "newListener".
if (this._events.newListener)
this.emit('newListener', type,
isFunction(listener.listener) ?
listener.listener : listener);
if (!this._events[type])
// Optimize the case of one listener. Don't need the extra array object.
this._events[type] = listener;
else if (isObject(this._events[type]))
// If we've already got an array, just append.
this._events[type].push(listener);
else
// Adding the second element, need to change to array.
this._events[type] = [this._events[type], listener];
// Check for listener leak
if (isObject(this._events[type]) && !this._events[type].warned) {
var m;
if (!isUndefined(this._maxListeners)) {
m = this._maxListeners;
} else {
m = EventEmitter.defaultMaxListeners;
}
if (m && m > 0 && this._events[type].length > m) {
this._events[type].warned = true;
console.error('(node) warning: possible EventEmitter memory ' +
'leak detected. %d listeners added. ' +
'Use emitter.setMaxListeners() to increase limit.',
this._events[type].length);
if (typeof console.trace === 'function') {
// not supported in IE 10
console.trace();
}
}
}
return this;
};
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.once = function(type, listener) {
if (!isFunction(listener))
throw TypeError('listener must be a function');
var fired = false;
function g() {
this.removeListener(type, g);
if (!fired) {
fired = true;
listener.apply(this, arguments);
}
}
g.listener = listener;
this.on(type, g);
return this;
};
// emits a 'removeListener' event iff the listener was removed
EventEmitter.prototype.removeListener = function(type, listener) {
var list, position, length, i;
if (!isFunction(listener))
throw TypeError('listener must be a function');
if (!this._events || !this._events[type])
return this;
list = this._events[type];
length = list.length;
position = -1;
if (list === listener ||
(isFunction(list.listener) && list.listener === listener)) {
delete this._events[type];
if (this._events.removeListener)
this.emit('removeListener', type, listener);
} else if (isObject(list)) {
for (i = length; i-- > 0;) {
if (list[i] === listener ||
(list[i].listener && list[i].listener === listener)) {
position = i;
break;
}
}
if (position < 0)
return this;
if (list.length === 1) {
list.length = 0;
delete this._events[type];
} else {
list.splice(position, 1);
}
if (this._events.removeListener)
this.emit('removeListener', type, listener);
}
return this;
};
EventEmitter.prototype.removeAllListeners = function(type) {
var key, listeners;
if (!this._events)
return this;
// not listening for removeListener, no need to emit
if (!this._events.removeListener) {
if (arguments.length === 0)
this._events = {};
else if (this._events[type])
delete this._events[type];
return this;
}
// emit removeListener for all listeners on all events
if (arguments.length === 0) {
for (key in this._events) {
if (key === 'removeListener') continue;
this.removeAllListeners(key);
}
this.removeAllListeners('removeListener');
this._events = {};
return this;
}
listeners = this._events[type];
if (isFunction(listeners)) {
this.removeListener(type, listeners);
} else {
// LIFO order
while (listeners.length)
this.removeListener(type, listeners[listeners.length - 1]);
}
delete this._events[type];
return this;
};
EventEmitter.prototype.listeners = function(type) {
var ret;
if (!this._events || !this._events[type])
ret = [];
else if (isFunction(this._events[type]))
ret = [this._events[type]];
else
ret = this._events[type].slice();
return ret;
};
EventEmitter.listenerCount = function(emitter, type) {
var ret;
if (!emitter._events || !emitter._events[type])
ret = 0;
else if (isFunction(emitter._events[type]))
ret = 1;
else
ret = emitter._events[type].length;
return ret;
};
function isFunction(arg) {
return typeof arg === 'function';
}
function isNumber(arg) {
return typeof arg === 'number';
}
function isObject(arg) {
return typeof arg === 'object' && arg !== null;
}
function isUndefined(arg) {
return arg === void 0;
}
},{}],5959:[function(require,module,exports){
var http = module.exports;
var EventEmitter = require('events').EventEmitter;
var Request = require('./lib/request');
var url = require('url')
http.request = function (params, cb) {
if (typeof params === 'string') {
params = url.parse(params)
}
if (!params) params = {};
if (!params.host && !params.port) {
params.port = parseInt(window.location.port, 10);
}
if (!params.host && params.hostname) {
params.host = params.hostname;
}
if (!params.scheme) params.scheme = window.location.protocol.split(':')[0];
if (!params.host) {
params.host = window.location.hostname || window.location.host;
}
if (/:/.test(params.host)) {
if (!params.port) {
params.port = params.host.split(':')[1];
}
params.host = params.host.split(':')[0];
}
if (!params.port) params.port = params.scheme == 'https' ? 443 : 80;
var req = new Request(new xhrHttp, params);
if (cb) req.on('response', cb);
return req;
};
http.get = function (params, cb) {
params.method = 'GET';
var req = http.request(params, cb);
req.end();
return req;
};
http.Agent = function () {};
http.Agent.defaultMaxSockets = 4;
var xhrHttp = (function () {
if (typeof window === 'undefined') {
throw new Error('no window object present');
}
else if (window.XMLHttpRequest) {
return window.XMLHttpRequest;
}
else if (window.ActiveXObject) {
var axs = [
'Msxml2.XMLHTTP.6.0',
'Msxml2.XMLHTTP.3.0',
'Microsoft.XMLHTTP'
];
for (var i = 0; i < axs.length; i++) {
try {
var ax = new(window.ActiveXObject)(axs[i]);
return function () {
if (ax) {
var ax_ = ax;
ax = null;
return ax_;
}
else {
return new(window.ActiveXObject)(axs[i]);
}
};
}
catch (e) {}
}
throw new Error('ajax not supported in this browser')
}
else {
throw new Error('ajax not supported in this browser');
}
})();
http.STATUS_CODES = {
100 : 'Continue',
101 : 'Switching Protocols',
102 : 'Processing', // RFC 2518, obsoleted by RFC 4918
200 : 'OK',
201 : 'Created',
202 : 'Accepted',
203 : 'Non-Authoritative Information',
204 : 'No Content',
205 : 'Reset Content',
206 : 'Partial Content',
207 : 'Multi-Status', // RFC 4918
300 : 'Multiple Choices',
301 : 'Moved Permanently',
302 : 'Moved Temporarily',
303 : 'See Other',
304 : 'Not Modified',
305 : 'Use Proxy',
307 : 'Temporary Redirect',
400 : 'Bad Request',
401 : 'Unauthorized',
402 : 'Payment Required',
403 : 'Forbidden',
404 : 'Not Found',
405 : 'Method Not Allowed',
406 : 'Not Acceptable',
407 : 'Proxy Authentication Required',
408 : 'Request Time-out',
409 : 'Conflict',
410 : 'Gone',
411 : 'Length Required',
412 : 'Precondition Failed',
413 : 'Request Entity Too Large',
414 : 'Request-URI Too Large',
415 : 'Unsupported Media Type',
416 : 'Requested Range Not Satisfiable',
417 : 'Expectation Failed',
418 : 'I\'m a teapot', // RFC 2324
422 : 'Unprocessable Entity', // RFC 4918
423 : 'Locked', // RFC 4918
424 : 'Failed Dependency', // RFC 4918
425 : 'Unordered Collection', // RFC 4918
426 : 'Upgrade Required', // RFC 2817
428 : 'Precondition Required', // RFC 6585
429 : 'Too Many Requests', // RFC 6585
431 : 'Request Header Fields Too Large',// RFC 6585
500 : 'Internal Server Error',
501 : 'Not Implemented',
502 : 'Bad Gateway',
503 : 'Service Unavailable',
504 : 'Gateway Time-out',
505 : 'HTTP Version Not Supported',
506 : 'Variant Also Negotiates', // RFC 2295
507 : 'Insufficient Storage', // RFC 4918
509 : 'Bandwidth Limit Exceeded',
510 : 'Not Extended', // RFC 2774
511 : 'Network Authentication Required' // RFC 6585
};
},{"./lib/request":5960,"events":5958,"url":5984}],5960:[function(require,module,exports){
var Stream = require('stream');
var Response = require('./response');
var Base64 = require('Base64');
var inherits = require('inherits');
var Request = module.exports = function (xhr, params) {
var self = this;
self.writable = true;
self.xhr = xhr;
self.body = [];
self.uri = (params.scheme || 'http') + '://'
+ params.host
+ (params.port ? ':' + params.port : '')
+ (params.path || '/')
;
if (typeof params.withCredentials === 'undefined') {
params.withCredentials = true;
}
try { xhr.withCredentials = params.withCredentials }
catch (e) {}
if (params.responseType) try { xhr.responseType = params.responseType }
catch (e) {}
xhr.open(
params.method || 'GET',
self.uri,
true
);
self._headers = {};
if (params.headers) {
var keys = objectKeys(params.headers);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (!self.isSafeRequestHeader(key)) continue;
var value = params.headers[key];
self.setHeader(key, value);
}
}
if (params.auth) {
//basic auth
this.setHeader('Authorization', 'Basic ' + Base64.btoa(params.auth));
}
var res = new Response;
res.on('close', function () {
self.emit('close');
});
res.on('ready', function () {
self.emit('response', res);
});
xhr.onreadystatechange = function () {
// Fix for IE9 bug
// SCRIPT575: Could not complete the operation due to error c00c023f
// It happens when a request is aborted, calling the success callback anyway with readyState === 4
if (xhr.__aborted) return;
res.handle(xhr);
};
};
inherits(Request, Stream);
Request.prototype.setHeader = function (key, value) {
this._headers[key.toLowerCase()] = value
};
Request.prototype.getHeader = function (key) {
return this._headers[key.toLowerCase()]
};
Request.prototype.removeHeader = function (key) {
delete this._headers[key.toLowerCase()]
};
Request.prototype.write = function (s) {
this.body.push(s);
};
Request.prototype.destroy = function (s) {
this.xhr.__aborted = true;
this.xhr.abort();
this.emit('close');
};
Request.prototype.end = function (s) {
if (s !== undefined) this.body.push(s);
var keys = objectKeys(this._headers);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var value = this._headers[key];
if (isArray(value)) {
for (var j = 0; j < value.length; j++) {
this.xhr.setRequestHeader(key, value[j]);
}
}
else this.xhr.setRequestHeader(key, value)
}
if (this.body.length === 0) {
this.xhr.send('');
}
else if (typeof this.body[0] === 'string') {
this.xhr.send(this.body.join(''));
}
else if (isArray(this.body[0])) {
var body = [];
for (var i = 0; i < this.body.length; i++) {
body.push.apply(body, this.body[i]);
}
this.xhr.send(body);
}
else if (/Array/.test(Object.prototype.toString.call(this.body[0]))) {
var len = 0;
for (var i = 0; i < this.body.length; i++) {
len += this.body[i].length;
}
var body = new(this.body[0].constructor)(len);
var k = 0;
for (var i = 0; i < this.body.length; i++) {
var b = this.body[i];
for (var j = 0; j < b.length; j++) {
body[k++] = b[j];
}
}
this.xhr.send(body);
}
else {
var body = '';
for (var i = 0; i < this.body.length; i++) {
body += this.body[i].toString();
}
this.xhr.send(body);
}
};
// Taken from http://dxr.mozilla.org/mozilla/mozilla-central/content/base/src/nsXMLHttpRequest.cpp.html
Request.unsafeHeaders = [
"accept-charset",
"accept-encoding",
"access-control-request-headers",
"access-control-request-method",
"connection",
"content-length",
"cookie",
"cookie2",
"content-transfer-encoding",
"date",
"expect",
"host",
"keep-alive",
"origin",
"referer",
"te",
"trailer",
"transfer-encoding",
"upgrade",
"user-agent",
"via"
];
Request.prototype.isSafeRequestHeader = function (headerName) {
if (!headerName) return false;
return indexOf(Request.unsafeHeaders, headerName.toLowerCase()) === -1;
};
var objectKeys = Object.keys || function (obj) {
var keys = [];
for (var key in obj) keys.push(key);
return keys;
};
var isArray = Array.isArray || function (xs) {
return Object.prototype.toString.call(xs) === '[object Array]';
};
var indexOf = function (xs, x) {
if (xs.indexOf) return xs.indexOf(x);
for (var i = 0; i < xs.length; i++) {
if (xs[i] === x) return i;
}
return -1;
};
},{"./response":5961,"Base64":5962,"inherits":5964,"stream":5983}],5961:[function(require,module,exports){
var Stream = require('stream');
var util = require('util');
var Response = module.exports = function (res) {
this.offset = 0;
this.readable = true;
};
util.inherits(Response, Stream);
var capable = {
streaming : true,
status2 : true
};
function parseHeaders (res) {
var lines = res.getAllResponseHeaders().split(/\r?\n/);
var headers = {};
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
if (line === '') continue;
var m = line.match(/^([^:]+):\s*(.*)/);
if (m) {
var key = m[1].toLowerCase(), value = m[2];
if (headers[key] !== undefined) {
if (isArray(headers[key])) {
headers[key].push(value);
}
else {
headers[key] = [ headers[key], value ];
}
}
else {
headers[key] = value;
}
}
else {
headers[line] = true;
}
}
return headers;
}
Response.prototype.getResponse = function (xhr) {
var respType = String(xhr.responseType).toLowerCase();
if (respType === 'blob') return xhr.responseBlob || xhr.response;
if (respType === 'arraybuffer') return xhr.response;
return xhr.responseText;
}
Response.prototype.getHeader = function (key) {
return this.headers[key.toLowerCase()];
};
Response.prototype.handle = function (res) {
if (res.readyState === 2 && capable.status2) {
try {
this.statusCode = res.status;
this.headers = parseHeaders(res);
}
catch (err) {
capable.status2 = false;
}
if (capable.status2) {
this.emit('ready');
}
}
else if (capable.streaming && res.readyState === 3) {
try {
if (!this.statusCode) {
this.statusCode = res.status;
this.headers = parseHeaders(res);
this.emit('ready');
}
}
catch (err) {}
try {
this._emitData(res);
}
catch (err) {
capable.streaming = false;
}
}
else if (res.readyState === 4) {
if (!this.statusCode) {
this.statusCode = res.status;
this.emit('ready');
}
this._emitData(res);
if (res.error) {
this.emit('error', this.getResponse(res));
}
else this.emit('end');
this.emit('close');
}
};
Response.prototype._emitData = function (res) {
var respBody = this.getResponse(res);
if (respBody.toString().match(/ArrayBuffer/)) {
this.emit('data', new Uint8Array(respBody, this.offset));
this.offset = respBody.byteLength;
return;
}
if (respBody.length > this.offset) {
this.emit('data', respBody.slice(this.offset));
this.offset = respBody.length;
}
};
var isArray = Array.isArray || function (xs) {
return Object.prototype.toString.call(xs) === '[object Array]';
};
},{"stream":5983,"util":5986}],5962:[function(require,module,exports){
;(function () {
var object = typeof exports != 'undefined' ? exports : this; // #8: web workers
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
function InvalidCharacterError(message) {
this.message = message;
}
InvalidCharacterError.prototype = new Error;
InvalidCharacterError.prototype.name = 'InvalidCharacterError';
// encoder
// [https://gist.github.com/999166] by [https://github.com/nignag]
object.btoa || (
object.btoa = function (input) {
for (
// initialize result and counter
var block, charCode, idx = 0, map = chars, output = '';
// if the next input index does not exist:
// change the mapping table to "="
// check if d has no fractional digits
input.charAt(idx | 0) || (map = '=', idx % 1);
// "8 - idx % 1 * 8" generates the sequence 2, 4, 6, 8
output += map.charAt(63 & block >> 8 - idx % 1 * 8)
) {
charCode = input.charCodeAt(idx += 3/4);
if (charCode > 0xFF) {
throw new InvalidCharacterError("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");
}
block = block << 8 | charCode;
}
return output;
});
// decoder
// [https://gist.github.com/1020396] by [https://github.com/atk]
object.atob || (
object.atob = function (input) {
input = input.replace(/=+$/, '');
if (input.length % 4 == 1) {
throw new InvalidCharacterError("'atob' failed: The string to be decoded is not correctly encoded.");
}
for (
// initialize result and counters
var bc = 0, bs, buffer, idx = 0, output = '';
// get next character
buffer = input.charAt(idx++);
// character found in table? initialize bit storage and add its ascii value;
~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
// and if not first of each 4 characters,
// convert the first 8 bits to one ascii character
bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
) {
// try to find character in table (0-63, not found => -1)
buffer = chars.indexOf(buffer);
}
return output;
});
}());
},{}],5963:[function(require,module,exports){
var http = require('http');
var https = module.exports;
for (var key in http) {
if (http.hasOwnProperty(key)) https[key] = http[key];
};
https.request = function (params, cb) {
if (!params) params = {};
params.scheme = 'https';
return http.request.call(this, params, cb);
}
},{"http":5959}],5964:[function(require,module,exports){
if (typeof Object.create === 'function') {
// implementation from standard node.js 'util' module
module.exports = function inherits(ctor, superCtor) {
ctor.super_ = superCtor
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
});
};
} else {
// old school shim for old browsers
module.exports = function inherits(ctor, superCtor) {
ctor.super_ = superCtor
var TempCtor = function () {}
TempCtor.prototype = superCtor.prototype
ctor.prototype = new TempCtor()
ctor.prototype.constructor = ctor
}
}
},{}],5965:[function(require,module,exports){
module.exports = Array.isArray || function (arr) {
return Object.prototype.toString.call(arr) == '[object Array]';
};
},{}],5966:[function(require,module,exports){
// shim for using process in browser
var process = module.exports = {};
process.nextTick = (function () {
var canSetImmediate = typeof window !== 'undefined'
&& window.setImmediate;
var canPost = typeof window !== 'undefined'
&& window.postMessage && window.addEventListener
;
if (canSetImmediate) {
return function (f) { return window.setImmediate(f) };
}
if (canPost) {
var queue = [];
window.addEventListener('message', function (ev) {
var source = ev.source;
if ((source === window || source === null) && ev.data === 'process-tick') {
ev.stopPropagation();
if (queue.length > 0) {
var fn = queue.shift();
fn();
}
}
}, true);
return function nextTick(fn) {
queue.push(fn);
window.postMessage('process-tick', '*');
};
}
return function nextTick(fn) {
setTimeout(fn, 0);
};
})();
process.title = 'browser';
process.browser = true;
process.env = {};
process.argv = [];
function noop() {}
process.on = noop;
process.addListener = noop;
process.once = noop;
process.off = noop;
process.removeListener = noop;
process.removeAllListeners = noop;
process.emit = noop;
process.binding = function (name) {
throw new Error('process.binding is not supported');
}
// TODO(shtylman)
process.cwd = function () { return '/' };
process.chdir = function (dir) {
throw new Error('process.chdir is not supported');
};
},{}],5967:[function(require,module,exports){
(function (global){
/*! http://mths.be/punycode v1.2.4 by @mathias */
;(function(root) {
/** Detect free variables */
var freeExports = typeof exports == 'object' && exports;
var freeModule = typeof module == 'object' && module &&
module.exports == freeExports && module;
var freeGlobal = typeof global == 'object' && global;
if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) {
root = freeGlobal;
}
/**
* The `punycode` object.
* @name punycode
* @type Object
*/
var punycode,
/** Highest positive signed 32-bit float value */
maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1
/** Bootstring parameters */
base = 36,
tMin = 1,
tMax = 26,
skew = 38,
damp = 700,
initialBias = 72,
initialN = 128, // 0x80
delimiter = '-', // '\x2D'
/** Regular expressions */
regexPunycode = /^xn--/,
regexNonASCII = /[^ -~]/, // unprintable ASCII chars + non-ASCII chars
regexSeparators = /\x2E|\u3002|\uFF0E|\uFF61/g, // RFC 3490 separators
/** Error messages */
errors = {
'overflow': 'Overflow: input needs wider integers to process',
'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
'invalid-input': 'Invalid input'
},
/** Convenience shortcuts */
baseMinusTMin = base - tMin,
floor = Math.floor,
stringFromCharCode = String.fromCharCode,
/** Temporary variable */
key;
/*--------------------------------------------------------------------------*/
/**
* A generic error utility function.
* @private
* @param {String} type The error type.
* @returns {Error} Throws a `RangeError` with the applicable error message.
*/
function error(type) {
throw RangeError(errors[type]);
}
/**
* A generic `Array#map` utility function.
* @private
* @param {Array} array The array to iterate over.
* @param {Function} callback The function that gets called for every array
* item.
* @returns {Array} A new array of values returned by the callback function.
*/
function map(array, fn) {
var length = array.length;
while (length--) {
array[length] = fn(array[length]);
}
return array;
}
/**
* A simple `Array#map`-like wrapper to work with domain name strings.
* @private
* @param {String} domain The domain name.
* @param {Function} callback The function that gets called for every
* character.
* @returns {Array} A new string of characters returned by the callback
* function.
*/
function mapDomain(string, fn) {
return map(string.split(regexSeparators), fn).join('.');
}
/**
* Creates an array containing the numeric code points of each Unicode
* character in the string. While JavaScript uses UCS-2 internally,
* this function will convert a pair of surrogate halves (each of which
* UCS-2 exposes as separate characters) into a single code point,
* matching UTF-16.
* @see `punycode.ucs2.encode`
* @see <http://mathiasbynens.be/notes/javascript-encoding>
* @memberOf punycode.ucs2
* @name decode
* @param {String} string The Unicode input string (UCS-2).
* @returns {Array} The new array of code points.
*/
function ucs2decode(string) {
var output = [],
counter = 0,
length = string.length,
value,
extra;
while (counter < length) {
value = string.charCodeAt(counter++);
if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
// high surrogate, and there is a next character
extra = string.charCodeAt(counter++);
if ((extra & 0xFC00) == 0xDC00) { // low surrogate
output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
} else {
// unmatched surrogate; only append this code unit, in case the next
// code unit is the high surrogate of a surrogate pair
output.push(value);
counter--;
}
} else {
output.push(value);
}
}
return output;
}
/**
* Creates a string based on an array of numeric code points.
* @see `punycode.ucs2.decode`
* @memberOf punycode.ucs2
* @name encode
* @param {Array} codePoints The array of numeric code points.
* @returns {String} The new Unicode string (UCS-2).
*/
function ucs2encode(array) {
return map(array, function(value) {
var output = '';
if (value > 0xFFFF) {
value -= 0x10000;
output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
value = 0xDC00 | value & 0x3FF;
}
output += stringFromCharCode(value);
return output;
}).join('');
}
/**
* Converts a basic code point into a digit/integer.
* @see `digitToBasic()`
* @private
* @param {Number} codePoint The basic numeric code point value.
* @returns {Number} The numeric value of a basic code point (for use in
* representing integers) in the range `0` to `base - 1`, or `base` if
* the code point does not represent a value.
*/
function basicToDigit(codePoint) {
if (codePoint - 48 < 10) {
return codePoint - 22;
}
if (codePoint - 65 < 26) {
return codePoint - 65;
}
if (codePoint - 97 < 26) {
return codePoint - 97;
}
return base;
}
/**
* Converts a digit/integer into a basic code point.
* @see `basicToDigit()`
* @private
* @param {Number} digit The numeric value of a basic code point.
* @returns {Number} The basic code point whose value (when used for
* representing integers) is `digit`, which needs to be in the range
* `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
* used; else, the lowercase form is used. The behavior is undefined
* if `flag` is non-zero and `digit` has no uppercase form.
*/
function digitToBasic(digit, flag) {
// 0..25 map to ASCII a..z or A..Z
// 26..35 map to ASCII 0..9
return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
}
/**
* Bias adaptation function as per section 3.4 of RFC 3492.
* http://tools.ietf.org/html/rfc3492#section-3.4
* @private
*/
function adapt(delta, numPoints, firstTime) {
var k = 0;
delta = firstTime ? floor(delta / damp) : delta >> 1;
delta += floor(delta / numPoints);
for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {
delta = floor(delta / baseMinusTMin);
}
return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
}
/**
* Converts a Punycode string of ASCII-only symbols to a string of Unicode
* symbols.
* @memberOf punycode
* @param {String} input The Punycode string of ASCII-only symbols.
* @returns {String} The resulting string of Unicode symbols.
*/
function decode(input) {
// Don't use UCS-2
var output = [],
inputLength = input.length,
out,
i = 0,
n = initialN,
bias = initialBias,
basic,
j,
index,
oldi,
w,
k,
digit,
t,
/** Cached calculation results */
baseMinusT;
// Handle the basic code points: let `basic` be the number of input code
// points before the last delimiter, or `0` if there is none, then copy
// the first basic code points to the output.
basic = input.lastIndexOf(delimiter);
if (basic < 0) {
basic = 0;
}
for (j = 0; j < basic; ++j) {
// if it's not a basic code point
if (input.charCodeAt(j) >= 0x80) {
error('not-basic');
}
output.push(input.charCodeAt(j));
}
// Main decoding loop: start just after the last delimiter if any basic code
// points were copied; start at the beginning otherwise.
for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {
// `index` is the index of the next character to be consumed.
// Decode a generalized variable-length integer into `delta`,
// which gets added to `i`. The overflow checking is easier
// if we increase `i` as we go, then subtract off its starting
// value at the end to obtain `delta`.
for (oldi = i, w = 1, k = base; /* no condition */; k += base) {
if (index >= inputLength) {
error('invalid-input');
}
digit = basicToDigit(input.charCodeAt(index++));
if (digit >= base || digit > floor((maxInt - i) / w)) {
error('overflow');
}
i += digit * w;
t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
if (digit < t) {
break;
}
baseMinusT = base - t;
if (w > floor(maxInt / baseMinusT)) {
error('overflow');
}
w *= baseMinusT;
}
out = output.length + 1;
bias = adapt(i - oldi, out, oldi == 0);
// `i` was supposed to wrap around from `out` to `0`,
// incrementing `n` each time, so we'll fix that now:
if (floor(i / out) > maxInt - n) {
error('overflow');
}
n += floor(i / out);
i %= out;
// Insert `n` at position `i` of the output
output.splice(i++, 0, n);
}
return ucs2encode(output);
}
/**
* Converts a string of Unicode symbols to a Punycode string of ASCII-only
* symbols.
* @memberOf punycode
* @param {String} input The string of Unicode symbols.
* @returns {String} The resulting Punycode string of ASCII-only symbols.
*/
function encode(input) {
var n,
delta,
handledCPCount,
basicLength,
bias,
j,
m,
q,
k,
t,
currentValue,
output = [],
/** `inputLength` will hold the number of code points in `input`. */
inputLength,
/** Cached calculation results */
handledCPCountPlusOne,
baseMinusT,
qMinusT;
// Convert the input in UCS-2 to Unicode
input = ucs2decode(input);
// Cache the length
inputLength = input.length;
// Initialize the state
n = initialN;
delta = 0;
bias = initialBias;
// Handle the basic code points
for (j = 0; j < inputLength; ++j) {
currentValue = input[j];
if (currentValue < 0x80) {
output.push(stringFromCharCode(currentValue));
}
}
handledCPCount = basicLength = output.length;
// `handledCPCount` is the number of code points that have been handled;
// `basicLength` is the number of basic code points.
// Finish the basic string - if it is not empty - with a delimiter
if (basicLength) {
output.push(delimiter);
}
// Main encoding loop:
while (handledCPCount < inputLength) {
// All non-basic code points < n have been handled already. Find the next
// larger one:
for (m = maxInt, j = 0; j < inputLength; ++j) {
currentValue = input[j];
if (currentValue >= n && currentValue < m) {
m = currentValue;
}
}
// Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
// but guard against overflow
handledCPCountPlusOne = handledCPCount + 1;
if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
error('overflow');
}
delta += (m - n) * handledCPCountPlusOne;
n = m;
for (j = 0; j < inputLength; ++j) {
currentValue = input[j];
if (currentValue < n && ++delta > maxInt) {
error('overflow');
}
if (currentValue == n) {
// Represent delta as a generalized variable-length integer
for (q = delta, k = base; /* no condition */; k += base) {
t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
if (q < t) {
break;
}
qMinusT = q - t;
baseMinusT = base - t;
output.push(
stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
);
q = floor(qMinusT / baseMinusT);
}
output.push(stringFromCharCode(digitToBasic(q, 0)));
bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
delta = 0;
++handledCPCount;
}
}
++delta;
++n;
}
return output.join('');
}
/**
* Converts a Punycode string representing a domain name to Unicode. Only the
* Punycoded parts of the domain name will be converted, i.e. it doesn't
* matter if you call it on a string that has already been converted to
* Unicode.
* @memberOf punycode
* @param {String} domain The Punycode domain name to convert to Unicode.
* @returns {String} The Unicode representation of the given Punycode
* string.
*/
function toUnicode(domain) {
return mapDomain(domain, function(string) {
return regexPunycode.test(string)
? decode(string.slice(4).toLowerCase())
: string;
});
}
/**
* Converts a Unicode string representing a domain name to Punycode. Only the
* non-ASCII parts of the domain name will be converted, i.e. it doesn't
* matter if you call it with a domain that's already in ASCII.
* @memberOf punycode
* @param {String} domain The domain name to convert, as a Unicode string.
* @returns {String} The Punycode representation of the given domain name.
*/
function toASCII(domain) {
return mapDomain(domain, function(string) {
return regexNonASCII.test(string)
? 'xn--' + encode(string)
: string;
});
}
/*--------------------------------------------------------------------------*/
/** Define the public API */
punycode = {
/**
* A string representing the current Punycode.js version number.
* @memberOf punycode
* @type String
*/
'version': '1.2.4',
/**
* An object of methods to convert from JavaScript's internal character
* representation (UCS-2) to Unicode code points, and back.
* @see <http://mathiasbynens.be/notes/javascript-encoding>
* @memberOf punycode
* @type Object
*/
'ucs2': {
'decode': ucs2decode,
'encode': ucs2encode
},
'decode': decode,
'encode': encode,
'toASCII': toASCII,
'toUnicode': toUnicode
};
/** Expose `punycode` */
// Some AMD build optimizers, like r.js, check for specific condition patterns
// like the following:
if (
typeof define == 'function' &&
typeof define.amd == 'object' &&
define.amd
) {
define('punycode', function() {
return punycode;
});
} else if (freeExports && !freeExports.nodeType) {
if (freeModule) { // in Node.js or RingoJS v0.8.0+
freeModule.exports = punycode;
} else { // in Narwhal or RingoJS v0.7.0-
for (key in punycode) {
punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]);
}
}
} else { // in Rhino or a web browser
root.punycode = punycode;
}
}(this));
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],5968:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
// If obj.hasOwnProperty has been overridden, then calling
// obj.hasOwnProperty(prop) will break.
// See: https://github.com/joyent/node/issues/1707
function hasOwnProperty(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
module.exports = function(qs, sep, eq, options) {
sep = sep || '&';
eq = eq || '=';
var obj = {};
if (typeof qs !== 'string' || qs.length === 0) {
return obj;
}
var regexp = /\+/g;
qs = qs.split(sep);
var maxKeys = 1000;
if (options && typeof options.maxKeys === 'number') {
maxKeys = options.maxKeys;
}
var len = qs.length;
// maxKeys <= 0 means that we should not limit keys count
if (maxKeys > 0 && len > maxKeys) {
len = maxKeys;
}
for (var i = 0; i < len; ++i) {
var x = qs[i].replace(regexp, '%20'),
idx = x.indexOf(eq),
kstr, vstr, k, v;
if (idx >= 0) {
kstr = x.substr(0, idx);
vstr = x.substr(idx + 1);
} else {
kstr = x;
vstr = '';
}
k = decodeURIComponent(kstr);
v = decodeURIComponent(vstr);
if (!hasOwnProperty(obj, k)) {
obj[k] = v;
} else if (isArray(obj[k])) {
obj[k].push(v);
} else {
obj[k] = [obj[k], v];
}
}
return obj;
};
var isArray = Array.isArray || function (xs) {
return Object.prototype.toString.call(xs) === '[object Array]';
};
},{}],5969:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
var stringifyPrimitive = function(v) {
switch (typeof v) {
case 'string':
return v;
case 'boolean':
return v ? 'true' : 'false';
case 'number':
return isFinite(v) ? v : '';
default:
return '';
}
};
module.exports = function(obj, sep, eq, name) {
sep = sep || '&';
eq = eq || '=';
if (obj === null) {
obj = undefined;
}
if (typeof obj === 'object') {
return map(objectKeys(obj), function(k) {
var ks = encodeURIComponent(stringifyPrimitive(k)) + eq;
if (isArray(obj[k])) {
return map(obj[k], function(v) {
return ks + encodeURIComponent(stringifyPrimitive(v));
}).join(sep);
} else {
return ks + encodeURIComponent(stringifyPrimitive(obj[k]));
}
}).join(sep);
}
if (!name) return '';
return encodeURIComponent(stringifyPrimitive(name)) + eq +
encodeURIComponent(stringifyPrimitive(obj));
};
var isArray = Array.isArray || function (xs) {
return Object.prototype.toString.call(xs) === '[object Array]';
};
function map (xs, f) {
if (xs.map) return xs.map(f);
var res = [];
for (var i = 0; i < xs.length; i++) {
res.push(f(xs[i], i));
}
return res;
}
var objectKeys = Object.keys || function (obj) {
var res = [];
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) res.push(key);
}
return res;
};
},{}],5970:[function(require,module,exports){
'use strict';
exports.decode = exports.parse = require('./decode');
exports.encode = exports.stringify = require('./encode');
},{"./decode":5968,"./encode":5969}],5971:[function(require,module,exports){
module.exports = require("./lib/_stream_duplex.js")
},{"./lib/_stream_duplex.js":5972}],5972:[function(require,module,exports){
(function (process){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// a duplex stream is just a stream that is both readable and writable.
// Since JS doesn't have multiple prototypal inheritance, this class
// prototypally inherits from Readable, and then parasitically from
// Writable.
module.exports = Duplex;
/*<replacement>*/
var objectKeys = Object.keys || function (obj) {
var keys = [];
for (var key in obj) keys.push(key);
return keys;
}
/*</replacement>*/
/*<replacement>*/
var util = require('core-util-is');
util.inherits = require('inherits');
/*</replacement>*/
var Readable = require('./_stream_readable');
var Writable = require('./_stream_writable');
util.inherits(Duplex, Readable);
forEach(objectKeys(Writable.prototype), function(method) {
if (!Duplex.prototype[method])
Duplex.prototype[method] = Writable.prototype[method];
});
function Duplex(options) {
if (!(this instanceof Duplex))
return new Duplex(options);
Readable.call(this, options);
Writable.call(this, options);
if (options && options.readable === false)
this.readable = false;
if (options && options.writable === false)
this.writable = false;
this.allowHalfOpen = true;
if (options && options.allowHalfOpen === false)
this.allowHalfOpen = false;
this.once('end', onend);
}
// the no-half-open enforcer
function onend() {
// if we allow half-open state, or if the writable side ended,
// then we're ok.
if (this.allowHalfOpen || this._writableState.ended)
return;
// no more data can be written.
// But allow more writes to happen in this tick.
process.nextTick(this.end.bind(this));
}
function forEach (xs, f) {
for (var i = 0, l = xs.length; i < l; i++) {
f(xs[i], i);
}
}
}).call(this,require('_process'))
},{"./_stream_readable":5974,"./_stream_writable":5976,"_process":5966,"core-util-is":5977,"inherits":5964}],5973:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// a passthrough stream.
// basically just the most minimal sort of Transform stream.
// Every written chunk gets output as-is.
module.exports = PassThrough;
var Transform = require('./_stream_transform');
/*<replacement>*/
var util = require('core-util-is');
util.inherits = require('inherits');
/*</replacement>*/
util.inherits(PassThrough, Transform);
function PassThrough(options) {
if (!(this instanceof PassThrough))
return new PassThrough(options);
Transform.call(this, options);
}
PassThrough.prototype._transform = function(chunk, encoding, cb) {
cb(null, chunk);
};
},{"./_stream_transform":5975,"core-util-is":5977,"inherits":5964}],5974:[function(require,module,exports){
(function (process){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
module.exports = Readable;
/*<replacement>*/
var isArray = require('isarray');
/*</replacement>*/
/*<replacement>*/
var Buffer = require('buffer').Buffer;
/*</replacement>*/
Readable.ReadableState = ReadableState;
var EE = require('events').EventEmitter;
/*<replacement>*/
if (!EE.listenerCount) EE.listenerCount = function(emitter, type) {
return emitter.listeners(type).length;
};
/*</replacement>*/
var Stream = require('stream');
/*<replacement>*/
var util = require('core-util-is');
util.inherits = require('inherits');
/*</replacement>*/
var StringDecoder;
util.inherits(Readable, Stream);
function ReadableState(options, stream) {
options = options || {};
// the point at which it stops calling _read() to fill the buffer
// Note: 0 is a valid value, means "don't call _read preemptively ever"
var hwm = options.highWaterMark;
this.highWaterMark = (hwm || hwm === 0) ? hwm : 16 * 1024;
// cast to ints.
this.highWaterMark = ~~this.highWaterMark;
this.buffer = [];
this.length = 0;
this.pipes = null;
this.pipesCount = 0;
this.flowing = false;
this.ended = false;
this.endEmitted = false;
this.reading = false;
// In streams that never have any data, and do push(null) right away,
// the consumer can miss the 'end' event if they do some I/O before
// consuming the stream. So, we don't emit('end') until some reading
// happens.
this.calledRead = false;
// a flag to be able to tell if the onwrite cb is called immediately,
// or on a later tick. We set this to true at first, becuase any
// actions that shouldn't happen until "later" should generally also
// not happen before the first write call.
this.sync = true;
// whenever we return null, then we set a flag to say
// that we're awaiting a 'readable' event emission.
this.needReadable = false;
this.emittedReadable = false;
this.readableListening = false;
// object stream flag. Used to make read(n) ignore n and to
// make all the buffer merging and length checks go away
this.objectMode = !!options.objectMode;
// Crypto is kind of old and crusty. Historically, its default string
// encoding is 'binary' so we have to make this configurable.
// Everything else in the universe uses 'utf8', though.
this.defaultEncoding = options.defaultEncoding || 'utf8';
// when piping, we only care about 'readable' events that happen
// after read()ing all the bytes and not getting any pushback.
this.ranOut = false;
// the number of writers that are awaiting a drain event in .pipe()s
this.awaitDrain = 0;
// if true, a maybeReadMore has been scheduled
this.readingMore = false;
this.decoder = null;
this.encoding = null;
if (options.encoding) {
if (!StringDecoder)
StringDecoder = require('string_decoder/').StringDecoder;
this.decoder = new StringDecoder(options.encoding);
this.encoding = options.encoding;
}
}
function Readable(options) {
if (!(this instanceof Readable))
return new Readable(options);
this._readableState = new ReadableState(options, this);
// legacy
this.readable = true;
Stream.call(this);
}
// Manually shove something into the read() buffer.
// This returns true if the highWaterMark has not been hit yet,
// similar to how Writable.write() returns true if you should
// write() some more.
Readable.prototype.push = function(chunk, encoding) {
var state = this._readableState;
if (typeof chunk === 'string' && !state.objectMode) {
encoding = encoding || state.defaultEncoding;
if (encoding !== state.encoding) {
chunk = new Buffer(chunk, encoding);
encoding = '';
}
}
return readableAddChunk(this, state, chunk, encoding, false);
};
// Unshift should *always* be something directly out of read()
Readable.prototype.unshift = function(chunk) {
var state = this._readableState;
return readableAddChunk(this, state, chunk, '', true);
};
function readableAddChunk(stream, state, chunk, encoding, addToFront) {
var er = chunkInvalid(state, chunk);
if (er) {
stream.emit('error', er);
} else if (chunk === null || chunk === undefined) {
state.reading = false;
if (!state.ended)
onEofChunk(stream, state);
} else if (state.objectMode || chunk && chunk.length > 0) {
if (state.ended && !addToFront) {
var e = new Error('stream.push() after EOF');
stream.emit('error', e);
} else if (state.endEmitted && addToFront) {
var e = new Error('stream.unshift() after end event');
stream.emit('error', e);
} else {
if (state.decoder && !addToFront && !encoding)
chunk = state.decoder.write(chunk);
// update the buffer info.
state.length += state.objectMode ? 1 : chunk.length;
if (addToFront) {
state.buffer.unshift(chunk);
} else {
state.reading = false;
state.buffer.push(chunk);
}
if (state.needReadable)
emitReadable(stream);
maybeReadMore(stream, state);
}
} else if (!addToFront) {
state.reading = false;
}
return needMoreData(state);
}
// if it's past the high water mark, we can push in some more.
// Also, if we have no data yet, we can stand some
// more bytes. This is to work around cases where hwm=0,
// such as the repl. Also, if the push() triggered a
// readable event, and the user called read(largeNumber) such that
// needReadable was set, then we ought to push more, so that another
// 'readable' event will be triggered.
function needMoreData(state) {
return !state.ended &&
(state.needReadable ||
state.length < state.highWaterMark ||
state.length === 0);
}
// backwards compatibility.
Readable.prototype.setEncoding = function(enc) {
if (!StringDecoder)
StringDecoder = require('string_decoder/').StringDecoder;
this._readableState.decoder = new StringDecoder(enc);
this._readableState.encoding = enc;
};
// Don't raise the hwm > 128MB
var MAX_HWM = 0x800000;
function roundUpToNextPowerOf2(n) {
if (n >= MAX_HWM) {
n = MAX_HWM;
} else {
// Get the next highest power of 2
n--;
for (var p = 1; p < 32; p <<= 1) n |= n >> p;
n++;
}
return n;
}
function howMuchToRead(n, state) {
if (state.length === 0 && state.ended)
return 0;
if (state.objectMode)
return n === 0 ? 0 : 1;
if (isNaN(n) || n === null) {
// only flow one buffer at a time
if (state.flowing && state.buffer.length)
return state.buffer[0].length;
else
return state.length;
}
if (n <= 0)
return 0;
// If we're asking for more than the target buffer level,
// then raise the water mark. Bump up to the next highest
// power of 2, to prevent increasing it excessively in tiny
// amounts.
if (n > state.highWaterMark)
state.highWaterMark = roundUpToNextPowerOf2(n);
// don't have that much. return null, unless we've ended.
if (n > state.length) {
if (!state.ended) {
state.needReadable = true;
return 0;
} else
return state.length;
}
return n;
}
// you can override either this method, or the async _read(n) below.
Readable.prototype.read = function(n) {
var state = this._readableState;
state.calledRead = true;
var nOrig = n;
if (typeof n !== 'number' || n > 0)
state.emittedReadable = false;
// if we're doing read(0) to trigger a readable event, but we
// already have a bunch of data in the buffer, then just trigger
// the 'readable' event and move on.
if (n === 0 &&
state.needReadable &&
(state.length >= state.highWaterMark || state.ended)) {
emitReadable(this);
return null;
}
n = howMuchToRead(n, state);
// if we've ended, and we're now clear, then finish it up.
if (n === 0 && state.ended) {
if (state.length === 0)
endReadable(this);
return null;
}
// All the actual chunk generation logic needs to be
// *below* the call to _read. The reason is that in certain
// synthetic stream cases, such as passthrough streams, _read
// may be a completely synchronous operation which may change
// the state of the read buffer, providing enough data when
// before there was *not* enough.
//
// So, the steps are:
// 1. Figure out what the state of things will be after we do
// a read from the buffer.
//
// 2. If that resulting state will trigger a _read, then call _read.
// Note that this may be asynchronous, or synchronous. Yes, it is
// deeply ugly to write APIs this way, but that still doesn't mean
// that the Readable class should behave improperly, as streams are
// designed to be sync/async agnostic.
// Take note if the _read call is sync or async (ie, if the read call
// has returned yet), so that we know whether or not it's safe to emit
// 'readable' etc.
//
// 3. Actually pull the requested chunks out of the buffer and return.
// if we need a readable event, then we need to do some reading.
var doRead = state.needReadable;
// if we currently have less than the highWaterMark, then also read some
if (state.length - n <= state.highWaterMark)
doRead = true;
// however, if we've ended, then there's no point, and if we're already
// reading, then it's unnecessary.
if (state.ended || state.reading)
doRead = false;
if (doRead) {
state.reading = true;
state.sync = true;
// if the length is currently zero, then we *need* a readable event.
if (state.length === 0)
state.needReadable = true;
// call internal read method
this._read(state.highWaterMark);
state.sync = false;
}
// If _read called its callback synchronously, then `reading`
// will be false, and we need to re-evaluate how much data we
// can return to the user.
if (doRead && !state.reading)
n = howMuchToRead(nOrig, state);
var ret;
if (n > 0)
ret = fromList(n, state);
else
ret = null;
if (ret === null) {
state.needReadable = true;
n = 0;
}
state.length -= n;
// If we have nothing in the buffer, then we want to know
// as soon as we *do* get something into the buffer.
if (state.length === 0 && !state.ended)
state.needReadable = true;
// If we happened to read() exactly the remaining amount in the
// buffer, and the EOF has been seen at this point, then make sure
// that we emit 'end' on the very next tick.
if (state.ended && !state.endEmitted && state.length === 0)
endReadable(this);
return ret;
};
function chunkInvalid(state, chunk) {
var er = null;
if (!Buffer.isBuffer(chunk) &&
'string' !== typeof chunk &&
chunk !== null &&
chunk !== undefined &&
!state.objectMode &&
!er) {
er = new TypeError('Invalid non-string/buffer chunk');
}
return er;
}
function onEofChunk(stream, state) {
if (state.decoder && !state.ended) {
var chunk = state.decoder.end();
if (chunk && chunk.length) {
state.buffer.push(chunk);
state.length += state.objectMode ? 1 : chunk.length;
}
}
state.ended = true;
// if we've ended and we have some data left, then emit
// 'readable' now to make sure it gets picked up.
if (state.length > 0)
emitReadable(stream);
else
endReadable(stream);
}
// Don't emit readable right away in sync mode, because this can trigger
// another read() call => stack overflow. This way, it might trigger
// a nextTick recursion warning, but that's not so bad.
function emitReadable(stream) {
var state = stream._readableState;
state.needReadable = false;
if (state.emittedReadable)
return;
state.emittedReadable = true;
if (state.sync)
process.nextTick(function() {
emitReadable_(stream);
});
else
emitReadable_(stream);
}
function emitReadable_(stream) {
stream.emit('readable');
}
// at this point, the user has presumably seen the 'readable' event,
// and called read() to consume some data. that may have triggered
// in turn another _read(n) call, in which case reading = true if
// it's in progress.
// However, if we're not ended, or reading, and the length < hwm,
// then go ahead and try to read some more preemptively.
function maybeReadMore(stream, state) {
if (!state.readingMore) {
state.readingMore = true;
process.nextTick(function() {
maybeReadMore_(stream, state);
});
}
}
function maybeReadMore_(stream, state) {
var len = state.length;
while (!state.reading && !state.flowing && !state.ended &&
state.length < state.highWaterMark) {
stream.read(0);
if (len === state.length)
// didn't get any data, stop spinning.
break;
else
len = state.length;
}
state.readingMore = false;
}
// abstract method. to be overridden in specific implementation classes.
// call cb(er, data) where data is <= n in length.
// for virtual (non-string, non-buffer) streams, "length" is somewhat
// arbitrary, and perhaps not very meaningful.
Readable.prototype._read = function(n) {
this.emit('error', new Error('not implemented'));
};
Readable.prototype.pipe = function(dest, pipeOpts) {
var src = this;
var state = this._readableState;
switch (state.pipesCount) {
case 0:
state.pipes = dest;
break;
case 1:
state.pipes = [state.pipes, dest];
break;
default:
state.pipes.push(dest);
break;
}
state.pipesCount += 1;
var doEnd = (!pipeOpts || pipeOpts.end !== false) &&
dest !== process.stdout &&
dest !== process.stderr;
var endFn = doEnd ? onend : cleanup;
if (state.endEmitted)
process.nextTick(endFn);
else
src.once('end', endFn);
dest.on('unpipe', onunpipe);
function onunpipe(readable) {
if (readable !== src) return;
cleanup();
}
function onend() {
dest.end();
}
// when the dest drains, it reduces the awaitDrain counter
// on the source. This would be more elegant with a .once()
// handler in flow(), but adding and removing repeatedly is
// too slow.
var ondrain = pipeOnDrain(src);
dest.on('drain', ondrain);
function cleanup() {
// cleanup event handlers once the pipe is broken
dest.removeListener('close', onclose);
dest.removeListener('finish', onfinish);
dest.removeListener('drain', ondrain);
dest.removeListener('error', onerror);
dest.removeListener('unpipe', onunpipe);
src.removeListener('end', onend);
src.removeListener('end', cleanup);
// if the reader is waiting for a drain event from this
// specific writer, then it would cause it to never start
// flowing again.
// So, if this is awaiting a drain, then we just call it now.
// If we don't know, then assume that we are waiting for one.
if (!dest._writableState || dest._writableState.needDrain)
ondrain();
}
// if the dest has an error, then stop piping into it.
// however, don't suppress the throwing behavior for this.
function onerror(er) {
unpipe();
dest.removeListener('error', onerror);
if (EE.listenerCount(dest, 'error') === 0)
dest.emit('error', er);
}
// This is a brutally ugly hack to make sure that our error handler
// is attached before any userland ones. NEVER DO THIS.
if (!dest._events || !dest._events.error)
dest.on('error', onerror);
else if (isArray(dest._events.error))
dest._events.error.unshift(onerror);
else
dest._events.error = [onerror, dest._events.error];
// Both close and finish should trigger unpipe, but only once.
function onclose() {
dest.removeListener('finish', onfinish);
unpipe();
}
dest.once('close', onclose);
function onfinish() {
dest.removeListener('close', onclose);
unpipe();
}
dest.once('finish', onfinish);
function unpipe() {
src.unpipe(dest);
}
// tell the dest that it's being piped to
dest.emit('pipe', src);
// start the flow if it hasn't been started already.
if (!state.flowing) {
// the handler that waits for readable events after all
// the data gets sucked out in flow.
// This would be easier to follow with a .once() handler
// in flow(), but that is too slow.
this.on('readable', pipeOnReadable);
state.flowing = true;
process.nextTick(function() {
flow(src);
});
}
return dest;
};
function pipeOnDrain(src) {
return function() {
var dest = this;
var state = src._readableState;
state.awaitDrain--;
if (state.awaitDrain === 0)
flow(src);
};
}
function flow(src) {
var state = src._readableState;
var chunk;
state.awaitDrain = 0;
function write(dest, i, list) {
var written = dest.write(chunk);
if (false === written) {
state.awaitDrain++;
}
}
while (state.pipesCount && null !== (chunk = src.read())) {
if (state.pipesCount === 1)
write(state.pipes, 0, null);
else
forEach(state.pipes, write);
src.emit('data', chunk);
// if anyone needs a drain, then we have to wait for that.
if (state.awaitDrain > 0)
return;
}
// if every destination was unpiped, either before entering this
// function, or in the while loop, then stop flowing.
//
// NB: This is a pretty rare edge case.
if (state.pipesCount === 0) {
state.flowing = false;
// if there were data event listeners added, then switch to old mode.
if (EE.listenerCount(src, 'data') > 0)
emitDataEvents(src);
return;
}
// at this point, no one needed a drain, so we just ran out of data
// on the next readable event, start it over again.
state.ranOut = true;
}
function pipeOnReadable() {
if (this._readableState.ranOut) {
this._readableState.ranOut = false;
flow(this);
}
}
Readable.prototype.unpipe = function(dest) {
var state = this._readableState;
// if we're not piping anywhere, then do nothing.
if (state.pipesCount === 0)
return this;
// just one destination. most common case.
if (state.pipesCount === 1) {
// passed in one, but it's not the right one.
if (dest && dest !== state.pipes)
return this;
if (!dest)
dest = state.pipes;
// got a match.
state.pipes = null;
state.pipesCount = 0;
this.removeListener('readable', pipeOnReadable);
state.flowing = false;
if (dest)
dest.emit('unpipe', this);
return this;
}
// slow case. multiple pipe destinations.
if (!dest) {
// remove all.
var dests = state.pipes;
var len = state.pipesCount;
state.pipes = null;
state.pipesCount = 0;
this.removeListener('readable', pipeOnReadable);
state.flowing = false;
for (var i = 0; i < len; i++)
dests[i].emit('unpipe', this);
return this;
}
// try to find the right one.
var i = indexOf(state.pipes, dest);
if (i === -1)
return this;
state.pipes.splice(i, 1);
state.pipesCount -= 1;
if (state.pipesCount === 1)
state.pipes = state.pipes[0];
dest.emit('unpipe', this);
return this;
};
// set up data events if they are asked for
// Ensure readable listeners eventually get something
Readable.prototype.on = function(ev, fn) {
var res = Stream.prototype.on.call(this, ev, fn);
if (ev === 'data' && !this._readableState.flowing)
emitDataEvents(this);
if (ev === 'readable' && this.readable) {
var state = this._readableState;
if (!state.readableListening) {
state.readableListening = true;
state.emittedReadable = false;
state.needReadable = true;
if (!state.reading) {
this.read(0);
} else if (state.length) {
emitReadable(this, state);
}
}
}
return res;
};
Readable.prototype.addListener = Readable.prototype.on;
// pause() and resume() are remnants of the legacy readable stream API
// If the user uses them, then switch into old mode.
Readable.prototype.resume = function() {
emitDataEvents(this);
this.read(0);
this.emit('resume');
};
Readable.prototype.pause = function() {
emitDataEvents(this, true);
this.emit('pause');
};
function emitDataEvents(stream, startPaused) {
var state = stream._readableState;
if (state.flowing) {
// https://github.com/isaacs/readable-stream/issues/16
throw new Error('Cannot switch to old mode now.');
}
var paused = startPaused || false;
var readable = false;
// convert to an old-style stream.
stream.readable = true;
stream.pipe = Stream.prototype.pipe;
stream.on = stream.addListener = Stream.prototype.on;
stream.on('readable', function() {
readable = true;
var c;
while (!paused && (null !== (c = stream.read())))
stream.emit('data', c);
if (c === null) {
readable = false;
stream._readableState.needReadable = true;
}
});
stream.pause = function() {
paused = true;
this.emit('pause');
};
stream.resume = function() {
paused = false;
if (readable)
process.nextTick(function() {
stream.emit('readable');
});
else
this.read(0);
this.emit('resume');
};
// now make it start, just in case it hadn't already.
stream.emit('readable');
}
// wrap an old-style stream as the async data source.
// This is *not* part of the readable stream interface.
// It is an ugly unfortunate mess of history.
Readable.prototype.wrap = function(stream) {
var state = this._readableState;
var paused = false;
var self = this;
stream.on('end', function() {
if (state.decoder && !state.ended) {
var chunk = state.decoder.end();
if (chunk && chunk.length)
self.push(chunk);
}
self.push(null);
});
stream.on('data', function(chunk) {
if (state.decoder)
chunk = state.decoder.write(chunk);
if (!chunk || !state.objectMode && !chunk.length)
return;
var ret = self.push(chunk);
if (!ret) {
paused = true;
stream.pause();
}
});
// proxy all the other methods.
// important when wrapping filters and duplexes.
for (var i in stream) {
if (typeof stream[i] === 'function' &&
typeof this[i] === 'undefined') {
this[i] = function(method) { return function() {
return stream[method].apply(stream, arguments);
}}(i);
}
}
// proxy certain important events.
var events = ['error', 'close', 'destroy', 'pause', 'resume'];
forEach(events, function(ev) {
stream.on(ev, self.emit.bind(self, ev));
});
// when we try to consume some more bytes, simply unpause the
// underlying stream.
self._read = function(n) {
if (paused) {
paused = false;
stream.resume();
}
};
return self;
};
// exposed for testing purposes only.
Readable._fromList = fromList;
// Pluck off n bytes from an array of buffers.
// Length is the combined lengths of all the buffers in the list.
function fromList(n, state) {
var list = state.buffer;
var length = state.length;
var stringMode = !!state.decoder;
var objectMode = !!state.objectMode;
var ret;
// nothing in the list, definitely empty.
if (list.length === 0)
return null;
if (length === 0)
ret = null;
else if (objectMode)
ret = list.shift();
else if (!n || n >= length) {
// read it all, truncate the array.
if (stringMode)
ret = list.join('');
else
ret = Buffer.concat(list, length);
list.length = 0;
} else {
// read just some of it.
if (n < list[0].length) {
// just take a part of the first list item.
// slice is the same for buffers and strings.
var buf = list[0];
ret = buf.slice(0, n);
list[0] = buf.slice(n);
} else if (n === list[0].length) {
// first list is a perfect match
ret = list.shift();
} else {
// complex case.
// we have enough to cover it, but it spans past the first buffer.
if (stringMode)
ret = '';
else
ret = new Buffer(n);
var c = 0;
for (var i = 0, l = list.length; i < l && c < n; i++) {
var buf = list[0];
var cpy = Math.min(n - c, buf.length);
if (stringMode)
ret += buf.slice(0, cpy);
else
buf.copy(ret, c, 0, cpy);
if (cpy < buf.length)
list[0] = buf.slice(cpy);
else
list.shift();
c += cpy;
}
}
}
return ret;
}
function endReadable(stream) {
var state = stream._readableState;
// If we get here before consuming all the bytes, then that is a
// bug in node. Should never happen.
if (state.length > 0)
throw new Error('endReadable called on non-empty stream');
if (!state.endEmitted && state.calledRead) {
state.ended = true;
process.nextTick(function() {
// Check that we didn't get one last unshift.
if (!state.endEmitted && state.length === 0) {
state.endEmitted = true;
stream.readable = false;
stream.emit('end');
}
});
}
}
function forEach (xs, f) {
for (var i = 0, l = xs.length; i < l; i++) {
f(xs[i], i);
}
}
function indexOf (xs, x) {
for (var i = 0, l = xs.length; i < l; i++) {
if (xs[i] === x) return i;
}
return -1;
}
}).call(this,require('_process'))
},{"_process":5966,"buffer":5955,"core-util-is":5977,"events":5958,"inherits":5964,"isarray":5965,"stream":5983,"string_decoder/":5978}],5975:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// a transform stream is a readable/writable stream where you do
// something with the data. Sometimes it's called a "filter",
// but that's not a great name for it, since that implies a thing where
// some bits pass through, and others are simply ignored. (That would
// be a valid example of a transform, of course.)
//
// While the output is causally related to the input, it's not a
// necessarily symmetric or synchronous transformation. For example,
// a zlib stream might take multiple plain-text writes(), and then
// emit a single compressed chunk some time in the future.
//
// Here's how this works:
//
// The Transform stream has all the aspects of the readable and writable
// stream classes. When you write(chunk), that calls _write(chunk,cb)
// internally, and returns false if there's a lot of pending writes
// buffered up. When you call read(), that calls _read(n) until
// there's enough pending readable data buffered up.
//
// In a transform stream, the written data is placed in a buffer. When
// _read(n) is called, it transforms the queued up data, calling the
// buffered _write cb's as it consumes chunks. If consuming a single
// written chunk would result in multiple output chunks, then the first
// outputted bit calls the readcb, and subsequent chunks just go into
// the read buffer, and will cause it to emit 'readable' if necessary.
//
// This way, back-pressure is actually determined by the reading side,
// since _read has to be called to start processing a new chunk. However,
// a pathological inflate type of transform can cause excessive buffering
// here. For example, imagine a stream where every byte of input is
// interpreted as an integer from 0-255, and then results in that many
// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in
// 1kb of data being output. In this case, you could write a very small
// amount of input, and end up with a very large amount of output. In
// such a pathological inflating mechanism, there'd be no way to tell
// the system to stop doing the transform. A single 4MB write could
// cause the system to run out of memory.
//
// However, even in such a pathological case, only a single written chunk
// would be consumed, and then the rest would wait (un-transformed) until
// the results of the previous transformed chunk were consumed.
module.exports = Transform;
var Duplex = require('./_stream_duplex');
/*<replacement>*/
var util = require('core-util-is');
util.inherits = require('inherits');
/*</replacement>*/
util.inherits(Transform, Duplex);
function TransformState(options, stream) {
this.afterTransform = function(er, data) {
return afterTransform(stream, er, data);
};
this.needTransform = false;
this.transforming = false;
this.writecb = null;
this.writechunk = null;
}
function afterTransform(stream, er, data) {
var ts = stream._transformState;
ts.transforming = false;
var cb = ts.writecb;
if (!cb)
return stream.emit('error', new Error('no writecb in Transform class'));
ts.writechunk = null;
ts.writecb = null;
if (data !== null && data !== undefined)
stream.push(data);
if (cb)
cb(er);
var rs = stream._readableState;
rs.reading = false;
if (rs.needReadable || rs.length < rs.highWaterMark) {
stream._read(rs.highWaterMark);
}
}
function Transform(options) {
if (!(this instanceof Transform))
return new Transform(options);
Duplex.call(this, options);
var ts = this._transformState = new TransformState(options, this);
// when the writable side finishes, then flush out anything remaining.
var stream = this;
// start out asking for a readable event once data is transformed.
this._readableState.needReadable = true;
// we have implemented the _read method, and done the other things
// that Readable wants before the first _read call, so unset the
// sync guard flag.
this._readableState.sync = false;
this.once('finish', function() {
if ('function' === typeof this._flush)
this._flush(function(er) {
done(stream, er);
});
else
done(stream);
});
}
Transform.prototype.push = function(chunk, encoding) {
this._transformState.needTransform = false;
return Duplex.prototype.push.call(this, chunk, encoding);
};
// This is the part where you do stuff!
// override this function in implementation classes.
// 'chunk' is an input chunk.
//
// Call `push(newChunk)` to pass along transformed output
// to the readable side. You may call 'push' zero or more times.
//
// Call `cb(err)` when you are done with this chunk. If you pass
// an error, then that'll put the hurt on the whole operation. If you
// never call cb(), then you'll never get another chunk.
Transform.prototype._transform = function(chunk, encoding, cb) {
throw new Error('not implemented');
};
Transform.prototype._write = function(chunk, encoding, cb) {
var ts = this._transformState;
ts.writecb = cb;
ts.writechunk = chunk;
ts.writeencoding = encoding;
if (!ts.transforming) {
var rs = this._readableState;
if (ts.needTransform ||
rs.needReadable ||
rs.length < rs.highWaterMark)
this._read(rs.highWaterMark);
}
};
// Doesn't matter what the args are here.
// _transform does all the work.
// That we got here means that the readable side wants more data.
Transform.prototype._read = function(n) {
var ts = this._transformState;
if (ts.writechunk !== null && ts.writecb && !ts.transforming) {
ts.transforming = true;
this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform);
} else {
// mark that we need a transform, so that any data that comes in
// will get processed, now that we've asked for it.
ts.needTransform = true;
}
};
function done(stream, er) {
if (er)
return stream.emit('error', er);
// if there's nothing in the write buffer, then that means
// that nothing more will ever be provided
var ws = stream._writableState;
var rs = stream._readableState;
var ts = stream._transformState;
if (ws.length)
throw new Error('calling transform done when ws.length != 0');
if (ts.transforming)
throw new Error('calling transform done when still transforming');
return stream.push(null);
}
},{"./_stream_duplex":5972,"core-util-is":5977,"inherits":5964}],5976:[function(require,module,exports){
(function (process){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// A bit simpler than readable streams.
// Implement an async ._write(chunk, cb), and it'll handle all
// the drain event emission and buffering.
module.exports = Writable;
/*<replacement>*/
var Buffer = require('buffer').Buffer;
/*</replacement>*/
Writable.WritableState = WritableState;
/*<replacement>*/
var util = require('core-util-is');
util.inherits = require('inherits');
/*</replacement>*/
var Stream = require('stream');
util.inherits(Writable, Stream);
function WriteReq(chunk, encoding, cb) {
this.chunk = chunk;
this.encoding = encoding;
this.callback = cb;
}
function WritableState(options, stream) {
options = options || {};
// the point at which write() starts returning false
// Note: 0 is a valid value, means that we always return false if
// the entire buffer is not flushed immediately on write()
var hwm = options.highWaterMark;
this.highWaterMark = (hwm || hwm === 0) ? hwm : 16 * 1024;
// object stream flag to indicate whether or not this stream
// contains buffers or objects.
this.objectMode = !!options.objectMode;
// cast to ints.
this.highWaterMark = ~~this.highWaterMark;
this.needDrain = false;
// at the start of calling end()
this.ending = false;
// when end() has been called, and returned
this.ended = false;
// when 'finish' is emitted
this.finished = false;
// should we decode strings into buffers before passing to _write?
// this is here so that some node-core streams can optimize string
// handling at a lower level.
var noDecode = options.decodeStrings === false;
this.decodeStrings = !noDecode;
// Crypto is kind of old and crusty. Historically, its default string
// encoding is 'binary' so we have to make this configurable.
// Everything else in the universe uses 'utf8', though.
this.defaultEncoding = options.defaultEncoding || 'utf8';
// not an actual buffer we keep track of, but a measurement
// of how much we're waiting to get pushed to some underlying
// socket or file.
this.length = 0;
// a flag to see when we're in the middle of a write.
this.writing = false;
// a flag to be able to tell if the onwrite cb is called immediately,
// or on a later tick. We set this to true at first, becuase any
// actions that shouldn't happen until "later" should generally also
// not happen before the first write call.
this.sync = true;
// a flag to know if we're processing previously buffered items, which
// may call the _write() callback in the same tick, so that we don't
// end up in an overlapped onwrite situation.
this.bufferProcessing = false;
// the callback that's passed to _write(chunk,cb)
this.onwrite = function(er) {
onwrite(stream, er);
};
// the callback that the user supplies to write(chunk,encoding,cb)
this.writecb = null;
// the amount that is being written when _write is called.
this.writelen = 0;
this.buffer = [];
// True if the error was already emitted and should not be thrown again
this.errorEmitted = false;
}
function Writable(options) {
var Duplex = require('./_stream_duplex');
// Writable ctor is applied to Duplexes, though they're not
// instanceof Writable, they're instanceof Readable.
if (!(this instanceof Writable) && !(this instanceof Duplex))
return new Writable(options);
this._writableState = new WritableState(options, this);
// legacy.
this.writable = true;
Stream.call(this);
}
// Otherwise people can pipe Writable streams, which is just wrong.
Writable.prototype.pipe = function() {
this.emit('error', new Error('Cannot pipe. Not readable.'));
};
function writeAfterEnd(stream, state, cb) {
var er = new Error('write after end');
// TODO: defer error events consistently everywhere, not just the cb
stream.emit('error', er);
process.nextTick(function() {
cb(er);
});
}
// If we get something that is not a buffer, string, null, or undefined,
// and we're not in objectMode, then that's an error.
// Otherwise stream chunks are all considered to be of length=1, and the
// watermarks determine how many objects to keep in the buffer, rather than
// how many bytes or characters.
function validChunk(stream, state, chunk, cb) {
var valid = true;
if (!Buffer.isBuffer(chunk) &&
'string' !== typeof chunk &&
chunk !== null &&
chunk !== undefined &&
!state.objectMode) {
var er = new TypeError('Invalid non-string/buffer chunk');
stream.emit('error', er);
process.nextTick(function() {
cb(er);
});
valid = false;
}
return valid;
}
Writable.prototype.write = function(chunk, encoding, cb) {
var state = this._writableState;
var ret = false;
if (typeof encoding === 'function') {
cb = encoding;
encoding = null;
}
if (Buffer.isBuffer(chunk))
encoding = 'buffer';
else if (!encoding)
encoding = state.defaultEncoding;
if (typeof cb !== 'function')
cb = function() {};
if (state.ended)
writeAfterEnd(this, state, cb);
else if (validChunk(this, state, chunk, cb))
ret = writeOrBuffer(this, state, chunk, encoding, cb);
return ret;
};
function decodeChunk(state, chunk, encoding) {
if (!state.objectMode &&
state.decodeStrings !== false &&
typeof chunk === 'string') {
chunk = new Buffer(chunk, encoding);
}
return chunk;
}
// if we're already writing something, then just put this
// in the queue, and wait our turn. Otherwise, call _write
// If we return false, then we need a drain event, so set that flag.
function writeOrBuffer(stream, state, chunk, encoding, cb) {
chunk = decodeChunk(state, chunk, encoding);
if (Buffer.isBuffer(chunk))
encoding = 'buffer';
var len = state.objectMode ? 1 : chunk.length;
state.length += len;
var ret = state.length < state.highWaterMark;
// we must ensure that previous needDrain will not be reset to false.
if (!ret)
state.needDrain = true;
if (state.writing)
state.buffer.push(new WriteReq(chunk, encoding, cb));
else
doWrite(stream, state, len, chunk, encoding, cb);
return ret;
}
function doWrite(stream, state, len, chunk, encoding, cb) {
state.writelen = len;
state.writecb = cb;
state.writing = true;
state.sync = true;
stream._write(chunk, encoding, state.onwrite);
state.sync = false;
}
function onwriteError(stream, state, sync, er, cb) {
if (sync)
process.nextTick(function() {
cb(er);
});
else
cb(er);
stream._writableState.errorEmitted = true;
stream.emit('error', er);
}
function onwriteStateUpdate(state) {
state.writing = false;
state.writecb = null;
state.length -= state.writelen;
state.writelen = 0;
}
function onwrite(stream, er) {
var state = stream._writableState;
var sync = state.sync;
var cb = state.writecb;
onwriteStateUpdate(state);
if (er)
onwriteError(stream, state, sync, er, cb);
else {
// Check if we're actually ready to finish, but don't emit yet
var finished = needFinish(stream, state);
if (!finished && !state.bufferProcessing && state.buffer.length)
clearBuffer(stream, state);
if (sync) {
process.nextTick(function() {
afterWrite(stream, state, finished, cb);
});
} else {
afterWrite(stream, state, finished, cb);
}
}
}
function afterWrite(stream, state, finished, cb) {
if (!finished)
onwriteDrain(stream, state);
cb();
if (finished)
finishMaybe(stream, state);
}
// Must force callback to be called on nextTick, so that we don't
// emit 'drain' before the write() consumer gets the 'false' return
// value, and has a chance to attach a 'drain' listener.
function onwriteDrain(stream, state) {
if (state.length === 0 && state.needDrain) {
state.needDrain = false;
stream.emit('drain');
}
}
// if there's something in the buffer waiting, then process it
function clearBuffer(stream, state) {
state.bufferProcessing = true;
for (var c = 0; c < state.buffer.length; c++) {
var entry = state.buffer[c];
var chunk = entry.chunk;
var encoding = entry.encoding;
var cb = entry.callback;
var len = state.objectMode ? 1 : chunk.length;
doWrite(stream, state, len, chunk, encoding, cb);
// if we didn't call the onwrite immediately, then
// it means that we need to wait until it does.
// also, that means that the chunk and cb are currently
// being processed, so move the buffer counter past them.
if (state.writing) {
c++;
break;
}
}
state.bufferProcessing = false;
if (c < state.buffer.length)
state.buffer = state.buffer.slice(c);
else
state.buffer.length = 0;
}
Writable.prototype._write = function(chunk, encoding, cb) {
cb(new Error('not implemented'));
};
Writable.prototype.end = function(chunk, encoding, cb) {
var state = this._writableState;
if (typeof chunk === 'function') {
cb = chunk;
chunk = null;
encoding = null;
} else if (typeof encoding === 'function') {
cb = encoding;
encoding = null;
}
if (typeof chunk !== 'undefined' && chunk !== null)
this.write(chunk, encoding);
// ignore unnecessary end() calls.
if (!state.ending && !state.finished)
endWritable(this, state, cb);
};
function needFinish(stream, state) {
return (state.ending &&
state.length === 0 &&
!state.finished &&
!state.writing);
}
function finishMaybe(stream, state) {
var need = needFinish(stream, state);
if (need) {
state.finished = true;
stream.emit('finish');
}
return need;
}
function endWritable(stream, state, cb) {
state.ending = true;
finishMaybe(stream, state);
if (cb) {
if (state.finished)
process.nextTick(cb);
else
stream.once('finish', cb);
}
state.ended = true;
}
}).call(this,require('_process'))
},{"./_stream_duplex":5972,"_process":5966,"buffer":5955,"core-util-is":5977,"inherits":5964,"stream":5983}],5977:[function(require,module,exports){
(function (Buffer){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// NOTE: These type checking functions intentionally don't use `instanceof`
// because it is fragile and can be easily faked with `Object.create()`.
function isArray(ar) {
return Array.isArray(ar);
}
exports.isArray = isArray;
function isBoolean(arg) {
return typeof arg === 'boolean';
}
exports.isBoolean = isBoolean;
function isNull(arg) {
return arg === null;
}
exports.isNull = isNull;
function isNullOrUndefined(arg) {
return arg == null;
}
exports.isNullOrUndefined = isNullOrUndefined;
function isNumber(arg) {
return typeof arg === 'number';
}
exports.isNumber = isNumber;
function isString(arg) {
return typeof arg === 'string';
}
exports.isString = isString;
function isSymbol(arg) {
return typeof arg === 'symbol';
}
exports.isSymbol = isSymbol;
function isUndefined(arg) {
return arg === void 0;
}
exports.isUndefined = isUndefined;
function isRegExp(re) {
return isObject(re) && objectToString(re) === '[object RegExp]';
}
exports.isRegExp = isRegExp;
function isObject(arg) {
return typeof arg === 'object' && arg !== null;
}
exports.isObject = isObject;
function isDate(d) {
return isObject(d) && objectToString(d) === '[object Date]';
}
exports.isDate = isDate;
function isError(e) {
return isObject(e) &&
(objectToString(e) === '[object Error]' || e instanceof Error);
}
exports.isError = isError;
function isFunction(arg) {
return typeof arg === 'function';
}
exports.isFunction = isFunction;
function isPrimitive(arg) {
return arg === null ||
typeof arg === 'boolean' ||
typeof arg === 'number' ||
typeof arg === 'string' ||
typeof arg === 'symbol' || // ES6 symbol
typeof arg === 'undefined';
}
exports.isPrimitive = isPrimitive;
function isBuffer(arg) {
return Buffer.isBuffer(arg);
}
exports.isBuffer = isBuffer;
function objectToString(o) {
return Object.prototype.toString.call(o);
}
}).call(this,require("buffer").Buffer)
},{"buffer":5955}],5978:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var Buffer = require('buffer').Buffer;
var isBufferEncoding = Buffer.isEncoding
|| function(encoding) {
switch (encoding && encoding.toLowerCase()) {
case 'hex': case 'utf8': case 'utf-8': case 'ascii': case 'binary': case 'base64': case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': case 'raw': return true;
default: return false;
}
}
function assertEncoding(encoding) {
if (encoding && !isBufferEncoding(encoding)) {
throw new Error('Unknown encoding: ' + encoding);
}
}
var StringDecoder = exports.StringDecoder = function(encoding) {
this.encoding = (encoding || 'utf8').toLowerCase().replace(/[-_]/, '');
assertEncoding(encoding);
switch (this.encoding) {
case 'utf8':
// CESU-8 represents each of Surrogate Pair by 3-bytes
this.surrogateSize = 3;
break;
case 'ucs2':
case 'utf16le':
// UTF-16 represents each of Surrogate Pair by 2-bytes
this.surrogateSize = 2;
this.detectIncompleteChar = utf16DetectIncompleteChar;
break;
case 'base64':
// Base-64 stores 3 bytes in 4 chars, and pads the remainder.
this.surrogateSize = 3;
this.detectIncompleteChar = base64DetectIncompleteChar;
break;
default:
this.write = passThroughWrite;
return;
}
this.charBuffer = new Buffer(6);
this.charReceived = 0;
this.charLength = 0;
};
StringDecoder.prototype.write = function(buffer) {
var charStr = '';
var offset = 0;
// if our last write ended with an incomplete multibyte character
while (this.charLength) {
// determine how many remaining bytes this buffer has to offer for this char
var i = (buffer.length >= this.charLength - this.charReceived) ?
this.charLength - this.charReceived :
buffer.length;
// add the new bytes to the char buffer
buffer.copy(this.charBuffer, this.charReceived, offset, i);
this.charReceived += (i - offset);
offset = i;
if (this.charReceived < this.charLength) {
// still not enough chars in this buffer? wait for more ...
return '';
}
// get the character that was split
charStr = this.charBuffer.slice(0, this.charLength).toString(this.encoding);
// lead surrogate (D800-DBFF) is also the incomplete character
var charCode = charStr.charCodeAt(charStr.length - 1);
if (charCode >= 0xD800 && charCode <= 0xDBFF) {
this.charLength += this.surrogateSize;
charStr = '';
continue;
}
this.charReceived = this.charLength = 0;
// if there are no more bytes in this buffer, just emit our char
if (i == buffer.length) return charStr;
// otherwise cut off the characters end from the beginning of this buffer
buffer = buffer.slice(i, buffer.length);
break;
}
var lenIncomplete = this.detectIncompleteChar(buffer);
var end = buffer.length;
if (this.charLength) {
// buffer the incomplete character bytes we got
buffer.copy(this.charBuffer, 0, buffer.length - lenIncomplete, end);
this.charReceived = lenIncomplete;
end -= lenIncomplete;
}
charStr += buffer.toString(this.encoding, 0, end);
var end = charStr.length - 1;
var charCode = charStr.charCodeAt(end);
// lead surrogate (D800-DBFF) is also the incomplete character
if (charCode >= 0xD800 && charCode <= 0xDBFF) {
var size = this.surrogateSize;
this.charLength += size;
this.charReceived += size;
this.charBuffer.copy(this.charBuffer, size, 0, size);
this.charBuffer.write(charStr.charAt(charStr.length - 1), this.encoding);
return charStr.substring(0, end);
}
// or just emit the charStr
return charStr;
};
StringDecoder.prototype.detectIncompleteChar = function(buffer) {
// determine how many bytes we have to check at the end of this buffer
var i = (buffer.length >= 3) ? 3 : buffer.length;
// Figure out if one of the last i bytes of our buffer announces an
// incomplete char.
for (; i > 0; i--) {
var c = buffer[buffer.length - i];
// See http://en.wikipedia.org/wiki/UTF-8#Description
// 110XXXXX
if (i == 1 && c >> 5 == 0x06) {
this.charLength = 2;
break;
}
// 1110XXXX
if (i <= 2 && c >> 4 == 0x0E) {
this.charLength = 3;
break;
}
// 11110XXX
if (i <= 3 && c >> 3 == 0x1E) {
this.charLength = 4;
break;
}
}
return i;
};
StringDecoder.prototype.end = function(buffer) {
var res = '';
if (buffer && buffer.length)
res = this.write(buffer);
if (this.charReceived) {
var cr = this.charReceived;
var buf = this.charBuffer;
var enc = this.encoding;
res += buf.slice(0, cr).toString(enc);
}
return res;
};
function passThroughWrite(buffer) {
return buffer.toString(this.encoding);
}
function utf16DetectIncompleteChar(buffer) {
var incomplete = this.charReceived = buffer.length % 2;
this.charLength = incomplete ? 2 : 0;
return incomplete;
}
function base64DetectIncompleteChar(buffer) {
var incomplete = this.charReceived = buffer.length % 3;
this.charLength = incomplete ? 3 : 0;
return incomplete;
}
},{"buffer":5955}],5979:[function(require,module,exports){
module.exports = require("./lib/_stream_passthrough.js")
},{"./lib/_stream_passthrough.js":5973}],5980:[function(require,module,exports){
exports = module.exports = require('./lib/_stream_readable.js');
exports.Readable = exports;
exports.Writable = require('./lib/_stream_writable.js');
exports.Duplex = require('./lib/_stream_duplex.js');
exports.Transform = require('./lib/_stream_transform.js');
exports.PassThrough = require('./lib/_stream_passthrough.js');
},{"./lib/_stream_duplex.js":5972,"./lib/_stream_passthrough.js":5973,"./lib/_stream_readable.js":5974,"./lib/_stream_transform.js":5975,"./lib/_stream_writable.js":5976}],5981:[function(require,module,exports){
module.exports = require("./lib/_stream_transform.js")
},{"./lib/_stream_transform.js":5975}],5982:[function(require,module,exports){
module.exports = require("./lib/_stream_writable.js")
},{"./lib/_stream_writable.js":5976}],5983:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
module.exports = Stream;
var EE = require('events').EventEmitter;
var inherits = require('inherits');
inherits(Stream, EE);
Stream.Readable = require('readable-stream/readable.js');
Stream.Writable = require('readable-stream/writable.js');
Stream.Duplex = require('readable-stream/duplex.js');
Stream.Transform = require('readable-stream/transform.js');
Stream.PassThrough = require('readable-stream/passthrough.js');
// Backwards-compat with node 0.4.x
Stream.Stream = Stream;
// old-style streams. Note that the pipe method (the only relevant
// part of this class) is overridden in the Readable class.
function Stream() {
EE.call(this);
}
Stream.prototype.pipe = function(dest, options) {
var source = this;
function ondata(chunk) {
if (dest.writable) {
if (false === dest.write(chunk) && source.pause) {
source.pause();
}
}
}
source.on('data', ondata);
function ondrain() {
if (source.readable && source.resume) {
source.resume();
}
}
dest.on('drain', ondrain);
// If the 'end' option is not supplied, dest.end() will be called when
// source gets the 'end' or 'close' events. Only dest.end() once.
if (!dest._isStdio && (!options || options.end !== false)) {
source.on('end', onend);
source.on('close', onclose);
}
var didOnEnd = false;
function onend() {
if (didOnEnd) return;
didOnEnd = true;
dest.end();
}
function onclose() {
if (didOnEnd) return;
didOnEnd = true;
if (typeof dest.destroy === 'function') dest.destroy();
}
// don't leave dangling pipes when there are errors.
function onerror(er) {
cleanup();
if (EE.listenerCount(this, 'error') === 0) {
throw er; // Unhandled stream error in pipe.
}
}
source.on('error', onerror);
dest.on('error', onerror);
// remove all the event listeners that were added.
function cleanup() {
source.removeListener('data', ondata);
dest.removeListener('drain', ondrain);
source.removeListener('end', onend);
source.removeListener('close', onclose);
source.removeListener('error', onerror);
dest.removeListener('error', onerror);
source.removeListener('end', cleanup);
source.removeListener('close', cleanup);
dest.removeListener('close', cleanup);
}
source.on('end', cleanup);
source.on('close', cleanup);
dest.on('close', cleanup);
dest.emit('pipe', source);
// Allow for unix-like usage: A.pipe(B).pipe(C)
return dest;
};
},{"events":5958,"inherits":5964,"readable-stream/duplex.js":5971,"readable-stream/passthrough.js":5979,"readable-stream/readable.js":5980,"readable-stream/transform.js":5981,"readable-stream/writable.js":5982}],5984:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var punycode = require('punycode');
exports.parse = urlParse;
exports.resolve = urlResolve;
exports.resolveObject = urlResolveObject;
exports.format = urlFormat;
exports.Url = Url;
function Url() {
this.protocol = null;
this.slashes = null;
this.auth = null;
this.host = null;
this.port = null;
this.hostname = null;
this.hash = null;
this.search = null;
this.query = null;
this.pathname = null;
this.path = null;
this.href = null;
}
// Reference: RFC 3986, RFC 1808, RFC 2396
// define these here so at least they only have to be
// compiled once on the first module load.
var protocolPattern = /^([a-z0-9.+-]+:)/i,
portPattern = /:[0-9]*$/,
// RFC 2396: characters reserved for delimiting URLs.
// We actually just auto-escape these.
delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t'],
// RFC 2396: characters not allowed for various reasons.
unwise = ['{', '}', '|', '\\', '^', '`'].concat(delims),
// Allowed by RFCs, but cause of XSS attacks. Always escape these.
autoEscape = ['\''].concat(unwise),
// Characters that are never ever allowed in a hostname.
// Note that any invalid chars are also handled, but these
// are the ones that are *expected* to be seen, so we fast-path
// them.
nonHostChars = ['%', '/', '?', ';', '#'].concat(autoEscape),
hostEndingChars = ['/', '?', '#'],
hostnameMaxLen = 255,
hostnamePartPattern = /^[a-z0-9A-Z_-]{0,63}$/,
hostnamePartStart = /^([a-z0-9A-Z_-]{0,63})(.*)$/,
// protocols that can allow "unsafe" and "unwise" chars.
unsafeProtocol = {
'javascript': true,
'javascript:': true
},
// protocols that never have a hostname.
hostlessProtocol = {
'javascript': true,
'javascript:': true
},
// protocols that always contain a // bit.
slashedProtocol = {
'http': true,
'https': true,
'ftp': true,
'gopher': true,
'file': true,
'http:': true,
'https:': true,
'ftp:': true,
'gopher:': true,
'file:': true
},
querystring = require('querystring');
function urlParse(url, parseQueryString, slashesDenoteHost) {
if (url && isObject(url) && url instanceof Url) return url;
var u = new Url;
u.parse(url, parseQueryString, slashesDenoteHost);
return u;
}
Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) {
if (!isString(url)) {
throw new TypeError("Parameter 'url' must be a string, not " + typeof url);
}
var rest = url;
// trim before proceeding.
// This is to support parse stuff like " http://foo.com \n"
rest = rest.trim();
var proto = protocolPattern.exec(rest);
if (proto) {
proto = proto[0];
var lowerProto = proto.toLowerCase();
this.protocol = lowerProto;
rest = rest.substr(proto.length);
}
// figure out if it's got a host
// user@server is *always* interpreted as a hostname, and url
// resolution will treat //foo/bar as host=foo,path=bar because that's
// how the browser resolves relative URLs.
if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) {
var slashes = rest.substr(0, 2) === '//';
if (slashes && !(proto && hostlessProtocol[proto])) {
rest = rest.substr(2);
this.slashes = true;
}
}
if (!hostlessProtocol[proto] &&
(slashes || (proto && !slashedProtocol[proto]))) {
// there's a hostname.
// the first instance of /, ?, ;, or # ends the host.
//
// If there is an @ in the hostname, then non-host chars *are* allowed
// to the left of the last @ sign, unless some host-ending character
// comes *before* the @-sign.
// URLs are obnoxious.
//
// ex:
// http://a@b@c/ => user:a@b host:c
// http://a@b?@c => user:a host:c path:/?@c
// v0.12 TODO(isaacs): This is not quite how Chrome does things.
// Review our test case against browsers more comprehensively.
// find the first instance of any hostEndingChars
var hostEnd = -1;
for (var i = 0; i < hostEndingChars.length; i++) {
var hec = rest.indexOf(hostEndingChars[i]);
if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
hostEnd = hec;
}
// at this point, either we have an explicit point where the
// auth portion cannot go past, or the last @ char is the decider.
var auth, atSign;
if (hostEnd === -1) {
// atSign can be anywhere.
atSign = rest.lastIndexOf('@');
} else {
// atSign must be in auth portion.
// http://a@b/c@d => host:b auth:a path:/c@d
atSign = rest.lastIndexOf('@', hostEnd);
}
// Now we have a portion which is definitely the auth.
// Pull that off.
if (atSign !== -1) {
auth = rest.slice(0, atSign);
rest = rest.slice(atSign + 1);
this.auth = decodeURIComponent(auth);
}
// the host is the remaining to the left of the first non-host char
hostEnd = -1;
for (var i = 0; i < nonHostChars.length; i++) {
var hec = rest.indexOf(nonHostChars[i]);
if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
hostEnd = hec;
}
// if we still have not hit it, then the entire thing is a host.
if (hostEnd === -1)
hostEnd = rest.length;
this.host = rest.slice(0, hostEnd);
rest = rest.slice(hostEnd);
// pull out port.
this.parseHost();
// we've indicated that there is a hostname,
// so even if it's empty, it has to be present.
this.hostname = this.hostname || '';
// if hostname begins with [ and ends with ]
// assume that it's an IPv6 address.
var ipv6Hostname = this.hostname[0] === '[' &&
this.hostname[this.hostname.length - 1] === ']';
// validate a little.
if (!ipv6Hostname) {
var hostparts = this.hostname.split(/\./);
for (var i = 0, l = hostparts.length; i < l; i++) {
var part = hostparts[i];
if (!part) continue;
if (!part.match(hostnamePartPattern)) {
var newpart = '';
for (var j = 0, k = part.length; j < k; j++) {
if (part.charCodeAt(j) > 127) {
// we replace non-ASCII char with a temporary placeholder
// we need this to make sure size of hostname is not
// broken by replacing non-ASCII by nothing
newpart += 'x';
} else {
newpart += part[j];
}
}
// we test again with ASCII char only
if (!newpart.match(hostnamePartPattern)) {
var validParts = hostparts.slice(0, i);
var notHost = hostparts.slice(i + 1);
var bit = part.match(hostnamePartStart);
if (bit) {
validParts.push(bit[1]);
notHost.unshift(bit[2]);
}
if (notHost.length) {
rest = '/' + notHost.join('.') + rest;
}
this.hostname = validParts.join('.');
break;
}
}
}
}
if (this.hostname.length > hostnameMaxLen) {
this.hostname = '';
} else {
// hostnames are always lower case.
this.hostname = this.hostname.toLowerCase();
}
if (!ipv6Hostname) {
// IDNA Support: Returns a puny coded representation of "domain".
// It only converts the part of the domain name that
// has non ASCII characters. I.e. it dosent matter if
// you call it with a domain that already is in ASCII.
var domainArray = this.hostname.split('.');
var newOut = [];
for (var i = 0; i < domainArray.length; ++i) {
var s = domainArray[i];
newOut.push(s.match(/[^A-Za-z0-9_-]/) ?
'xn--' + punycode.encode(s) : s);
}
this.hostname = newOut.join('.');
}
var p = this.port ? ':' + this.port : '';
var h = this.hostname || '';
this.host = h + p;
this.href += this.host;
// strip [ and ] from the hostname
// the host field still retains them, though
if (ipv6Hostname) {
this.hostname = this.hostname.substr(1, this.hostname.length - 2);
if (rest[0] !== '/') {
rest = '/' + rest;
}
}
}
// now rest is set to the post-host stuff.
// chop off any delim chars.
if (!unsafeProtocol[lowerProto]) {
// First, make 100% sure that any "autoEscape" chars get
// escaped, even if encodeURIComponent doesn't think they
// need to be.
for (var i = 0, l = autoEscape.length; i < l; i++) {
var ae = autoEscape[i];
var esc = encodeURIComponent(ae);
if (esc === ae) {
esc = escape(ae);
}
rest = rest.split(ae).join(esc);
}
}
// chop off from the tail first.
var hash = rest.indexOf('#');
if (hash !== -1) {
// got a fragment string.
this.hash = rest.substr(hash);
rest = rest.slice(0, hash);
}
var qm = rest.indexOf('?');
if (qm !== -1) {
this.search = rest.substr(qm);
this.query = rest.substr(qm + 1);
if (parseQueryString) {
this.query = querystring.parse(this.query);
}
rest = rest.slice(0, qm);
} else if (parseQueryString) {
// no query string, but parseQueryString still requested
this.search = '';
this.query = {};
}
if (rest) this.pathname = rest;
if (slashedProtocol[lowerProto] &&
this.hostname && !this.pathname) {
this.pathname = '/';
}
//to support http.request
if (this.pathname || this.search) {
var p = this.pathname || '';
var s = this.search || '';
this.path = p + s;
}
// finally, reconstruct the href based on what has been validated.
this.href = this.format();
return this;
};
// format a parsed object into a url string
function urlFormat(obj) {
// ensure it's an object, and not a string url.
// If it's an obj, this is a no-op.
// this way, you can call url_format() on strings
// to clean up potentially wonky urls.
if (isString(obj)) obj = urlParse(obj);
if (!(obj instanceof Url)) return Url.prototype.format.call(obj);
return obj.format();
}
Url.prototype.format = function() {
var auth = this.auth || '';
if (auth) {
auth = encodeURIComponent(auth);
auth = auth.replace(/%3A/i, ':');
auth += '@';
}
var protocol = this.protocol || '',
pathname = this.pathname || '',
hash = this.hash || '',
host = false,
query = '';
if (this.host) {
host = auth + this.host;
} else if (this.hostname) {
host = auth + (this.hostname.indexOf(':') === -1 ?
this.hostname :
'[' + this.hostname + ']');
if (this.port) {
host += ':' + this.port;
}
}
if (this.query &&
isObject(this.query) &&
Object.keys(this.query).length) {
query = querystring.stringify(this.query);
}
var search = this.search || (query && ('?' + query)) || '';
if (protocol && protocol.substr(-1) !== ':') protocol += ':';
// only the slashedProtocols get the //. Not mailto:, xmpp:, etc.
// unless they had them to begin with.
if (this.slashes ||
(!protocol || slashedProtocol[protocol]) && host !== false) {
host = '//' + (host || '');
if (pathname && pathname.charAt(0) !== '/') pathname = '/' + pathname;
} else if (!host) {
host = '';
}
if (hash && hash.charAt(0) !== '#') hash = '#' + hash;
if (search && search.charAt(0) !== '?') search = '?' + search;
pathname = pathname.replace(/[?#]/g, function(match) {
return encodeURIComponent(match);
});
search = search.replace('#', '%23');
return protocol + host + pathname + search + hash;
};
function urlResolve(source, relative) {
return urlParse(source, false, true).resolve(relative);
}
Url.prototype.resolve = function(relative) {
return this.resolveObject(urlParse(relative, false, true)).format();
};
function urlResolveObject(source, relative) {
if (!source) return relative;
return urlParse(source, false, true).resolveObject(relative);
}
Url.prototype.resolveObject = function(relative) {
if (isString(relative)) {
var rel = new Url();
rel.parse(relative, false, true);
relative = rel;
}
var result = new Url();
Object.keys(this).forEach(function(k) {
result[k] = this[k];
}, this);
// hash is always overridden, no matter what.
// even href="" will remove it.
result.hash = relative.hash;
// if the relative url is empty, then there's nothing left to do here.
if (relative.href === '') {
result.href = result.format();
return result;
}
// hrefs like //foo/bar always cut to the protocol.
if (relative.slashes && !relative.protocol) {
// take everything except the protocol from relative
Object.keys(relative).forEach(function(k) {
if (k !== 'protocol')
result[k] = relative[k];
});
//urlParse appends trailing / to urls like http://www.example.com
if (slashedProtocol[result.protocol] &&
result.hostname && !result.pathname) {
result.path = result.pathname = '/';
}
result.href = result.format();
return result;
}
if (relative.protocol && relative.protocol !== result.protocol) {
// if it's a known url protocol, then changing
// the protocol does weird things
// first, if it's not file:, then we MUST have a host,
// and if there was a path
// to begin with, then we MUST have a path.
// if it is file:, then the host is dropped,
// because that's known to be hostless.
// anything else is assumed to be absolute.
if (!slashedProtocol[relative.protocol]) {
Object.keys(relative).forEach(function(k) {
result[k] = relative[k];
});
result.href = result.format();
return result;
}
result.protocol = relative.protocol;
if (!relative.host && !hostlessProtocol[relative.protocol]) {
var relPath = (relative.pathname || '').split('/');
while (relPath.length && !(relative.host = relPath.shift()));
if (!relative.host) relative.host = '';
if (!relative.hostname) relative.hostname = '';
if (relPath[0] !== '') relPath.unshift('');
if (relPath.length < 2) relPath.unshift('');
result.pathname = relPath.join('/');
} else {
result.pathname = relative.pathname;
}
result.search = relative.search;
result.query = relative.query;
result.host = relative.host || '';
result.auth = relative.auth;
result.hostname = relative.hostname || relative.host;
result.port = relative.port;
// to support http.request
if (result.pathname || result.search) {
var p = result.pathname || '';
var s = result.search || '';
result.path = p + s;
}
result.slashes = result.slashes || relative.slashes;
result.href = result.format();
return result;
}
var isSourceAbs = (result.pathname && result.pathname.charAt(0) === '/'),
isRelAbs = (
relative.host ||
relative.pathname && relative.pathname.charAt(0) === '/'
),
mustEndAbs = (isRelAbs || isSourceAbs ||
(result.host && relative.pathname)),
removeAllDots = mustEndAbs,
srcPath = result.pathname && result.pathname.split('/') || [],
relPath = relative.pathname && relative.pathname.split('/') || [],
psychotic = result.protocol && !slashedProtocol[result.protocol];
// if the url is a non-slashed url, then relative
// links like ../.. should be able
// to crawl up to the hostname, as well. This is strange.
// result.protocol has already been set by now.
// Later on, put the first path part into the host field.
if (psychotic) {
result.hostname = '';
result.port = null;
if (result.host) {
if (srcPath[0] === '') srcPath[0] = result.host;
else srcPath.unshift(result.host);
}
result.host = '';
if (relative.protocol) {
relative.hostname = null;
relative.port = null;
if (relative.host) {
if (relPath[0] === '') relPath[0] = relative.host;
else relPath.unshift(relative.host);
}
relative.host = null;
}
mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === '');
}
if (isRelAbs) {
// it's absolute.
result.host = (relative.host || relative.host === '') ?
relative.host : result.host;
result.hostname = (relative.hostname || relative.hostname === '') ?
relative.hostname : result.hostname;
result.search = relative.search;
result.query = relative.query;
srcPath = relPath;
// fall through to the dot-handling below.
} else if (relPath.length) {
// it's relative
// throw away the existing file, and take the new path instead.
if (!srcPath) srcPath = [];
srcPath.pop();
srcPath = srcPath.concat(relPath);
result.search = relative.search;
result.query = relative.query;
} else if (!isNullOrUndefined(relative.search)) {
// just pull out the search.
// like href='?foo'.
// Put this after the other two cases because it simplifies the booleans
if (psychotic) {
result.hostname = result.host = srcPath.shift();
//occationaly the auth can get stuck only in host
//this especialy happens in cases like
//url.resolveObject('mailto:local1@domain1', 'local2@domain2')
var authInHost = result.host && result.host.indexOf('@') > 0 ?
result.host.split('@') : false;
if (authInHost) {
result.auth = authInHost.shift();
result.host = result.hostname = authInHost.shift();
}
}
result.search = relative.search;
result.query = relative.query;
//to support http.request
if (!isNull(result.pathname) || !isNull(result.search)) {
result.path = (result.pathname ? result.pathname : '') +
(result.search ? result.search : '');
}
result.href = result.format();
return result;
}
if (!srcPath.length) {
// no path at all. easy.
// we've already handled the other stuff above.
result.pathname = null;
//to support http.request
if (result.search) {
result.path = '/' + result.search;
} else {
result.path = null;
}
result.href = result.format();
return result;
}
// if a url ENDs in . or .., then it must get a trailing slash.
// however, if it ends in anything else non-slashy,
// then it must NOT get a trailing slash.
var last = srcPath.slice(-1)[0];
var hasTrailingSlash = (
(result.host || relative.host) && (last === '.' || last === '..') ||
last === '');
// strip single dots, resolve double dots to parent dir
// if the path tries to go above the root, `up` ends up > 0
var up = 0;
for (var i = srcPath.length; i >= 0; i--) {
last = srcPath[i];
if (last == '.') {
srcPath.splice(i, 1);
} else if (last === '..') {
srcPath.splice(i, 1);
up++;
} else if (up) {
srcPath.splice(i, 1);
up--;
}
}
// if the path is allowed to go above the root, restore leading ..s
if (!mustEndAbs && !removeAllDots) {
for (; up--; up) {
srcPath.unshift('..');
}
}
if (mustEndAbs && srcPath[0] !== '' &&
(!srcPath[0] || srcPath[0].charAt(0) !== '/')) {
srcPath.unshift('');
}
if (hasTrailingSlash && (srcPath.join('/').substr(-1) !== '/')) {
srcPath.push('');
}
var isAbsolute = srcPath[0] === '' ||
(srcPath[0] && srcPath[0].charAt(0) === '/');
// put the host back
if (psychotic) {
result.hostname = result.host = isAbsolute ? '' :
srcPath.length ? srcPath.shift() : '';
//occationaly the auth can get stuck only in host
//this especialy happens in cases like
//url.resolveObject('mailto:local1@domain1', 'local2@domain2')
var authInHost = result.host && result.host.indexOf('@') > 0 ?
result.host.split('@') : false;
if (authInHost) {
result.auth = authInHost.shift();
result.host = result.hostname = authInHost.shift();
}
}
mustEndAbs = mustEndAbs || (result.host && srcPath.length);
if (mustEndAbs && !isAbsolute) {
srcPath.unshift('');
}
if (!srcPath.length) {
result.pathname = null;
result.path = null;
} else {
result.pathname = srcPath.join('/');
}
//to support request.http
if (!isNull(result.pathname) || !isNull(result.search)) {
result.path = (result.pathname ? result.pathname : '') +
(result.search ? result.search : '');
}
result.auth = relative.auth || result.auth;
result.slashes = result.slashes || relative.slashes;
result.href = result.format();
return result;
};
Url.prototype.parseHost = function() {
var host = this.host;
var port = portPattern.exec(host);
if (port) {
port = port[0];
if (port !== ':') {
this.port = port.substr(1);
}
host = host.substr(0, host.length - port.length);
}
if (host) this.hostname = host;
};
function isString(arg) {
return typeof arg === "string";
}
function isObject(arg) {
return typeof arg === 'object' && arg !== null;
}
function isNull(arg) {
return arg === null;
}
function isNullOrUndefined(arg) {
return arg == null;
}
},{"punycode":5967,"querystring":5970}],5985:[function(require,module,exports){
module.exports = function isBuffer(arg) {
return arg && typeof arg === 'object'
&& typeof arg.copy === 'function'
&& typeof arg.fill === 'function'
&& typeof arg.readUInt8 === 'function';
}
},{}],5986:[function(require,module,exports){
(function (process,global){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var formatRegExp = /%[sdj%]/g;
exports.format = function(f) {
if (!isString(f)) {
var objects = [];
for (var i = 0; i < arguments.length; i++) {
objects.push(inspect(arguments[i]));
}
return objects.join(' ');
}
var i = 1;
var args = arguments;
var len = args.length;
var str = String(f).replace(formatRegExp, function(x) {
if (x === '%%') return '%';
if (i >= len) return x;
switch (x) {
case '%s': return String(args[i++]);
case '%d': return Number(args[i++]);
case '%j':
try {
return JSON.stringify(args[i++]);
} catch (_) {
return '[Circular]';
}
default:
return x;
}
});
for (var x = args[i]; i < len; x = args[++i]) {
if (isNull(x) || !isObject(x)) {
str += ' ' + x;
} else {
str += ' ' + inspect(x);
}
}
return str;
};
// Mark that a method should not be used.
// Returns a modified function which warns once by default.
// If --no-deprecation is set, then it is a no-op.
exports.deprecate = function(fn, msg) {
// Allow for deprecating things in the process of starting up.
if (isUndefined(global.process)) {
return function() {
return exports.deprecate(fn, msg).apply(this, arguments);
};
}
if (process.noDeprecation === true) {
return fn;
}
var warned = false;
function deprecated() {
if (!warned) {
if (process.throwDeprecation) {
throw new Error(msg);
} else if (process.traceDeprecation) {
console.trace(msg);
} else {
console.error(msg);
}
warned = true;
}
return fn.apply(this, arguments);
}
return deprecated;
};
var debugs = {};
var debugEnviron;
exports.debuglog = function(set) {
if (isUndefined(debugEnviron))
debugEnviron = process.env.NODE_DEBUG || '';
set = set.toUpperCase();
if (!debugs[set]) {
if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) {
var pid = process.pid;
debugs[set] = function() {
var msg = exports.format.apply(exports, arguments);
console.error('%s %d: %s', set, pid, msg);
};
} else {
debugs[set] = function() {};
}
}
return debugs[set];
};
/**
* Echos the value of a value. Trys to print the value out
* in the best way possible given the different types.
*
* @param {Object} obj The object to print out.
* @param {Object} opts Optional options object that alters the output.
*/
/* legacy: obj, showHidden, depth, colors*/
function inspect(obj, opts) {
// default options
var ctx = {
seen: [],
stylize: stylizeNoColor
};
// legacy...
if (arguments.length >= 3) ctx.depth = arguments[2];
if (arguments.length >= 4) ctx.colors = arguments[3];
if (isBoolean(opts)) {
// legacy...
ctx.showHidden = opts;
} else if (opts) {
// got an "options" object
exports._extend(ctx, opts);
}
// set default options
if (isUndefined(ctx.showHidden)) ctx.showHidden = false;
if (isUndefined(ctx.depth)) ctx.depth = 2;
if (isUndefined(ctx.colors)) ctx.colors = false;
if (isUndefined(ctx.customInspect)) ctx.customInspect = true;
if (ctx.colors) ctx.stylize = stylizeWithColor;
return formatValue(ctx, obj, ctx.depth);
}
exports.inspect = inspect;
// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
inspect.colors = {
'bold' : [1, 22],
'italic' : [3, 23],
'underline' : [4, 24],
'inverse' : [7, 27],
'white' : [37, 39],
'grey' : [90, 39],
'black' : [30, 39],
'blue' : [34, 39],
'cyan' : [36, 39],
'green' : [32, 39],
'magenta' : [35, 39],
'red' : [31, 39],
'yellow' : [33, 39]
};
// Don't use 'blue' not visible on cmd.exe
inspect.styles = {
'special': 'cyan',
'number': 'yellow',
'boolean': 'yellow',
'undefined': 'grey',
'null': 'bold',
'string': 'green',
'date': 'magenta',
// "name": intentionally not styling
'regexp': 'red'
};
function stylizeWithColor(str, styleType) {
var style = inspect.styles[styleType];
if (style) {
return '\u001b[' + inspect.colors[style][0] + 'm' + str +
'\u001b[' + inspect.colors[style][1] + 'm';
} else {
return str;
}
}
function stylizeNoColor(str, styleType) {
return str;
}
function arrayToHash(array) {
var hash = {};
array.forEach(function(val, idx) {
hash[val] = true;
});
return hash;
}
function formatValue(ctx, value, recurseTimes) {
// Provide a hook for user-specified inspect functions.
// Check that value is an object with an inspect function on it
if (ctx.customInspect &&
value &&
isFunction(value.inspect) &&
// Filter out the util module, it's inspect function is special
value.inspect !== exports.inspect &&
// Also filter out any prototype objects using the circular check.
!(value.constructor && value.constructor.prototype === value)) {
var ret = value.inspect(recurseTimes, ctx);
if (!isString(ret)) {
ret = formatValue(ctx, ret, recurseTimes);
}
return ret;
}
// Primitive types cannot have properties
var primitive = formatPrimitive(ctx, value);
if (primitive) {
return primitive;
}
// Look up the keys of the object.
var keys = Object.keys(value);
var visibleKeys = arrayToHash(keys);
if (ctx.showHidden) {
keys = Object.getOwnPropertyNames(value);
}
// IE doesn't make error fields non-enumerable
// http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx
if (isError(value)
&& (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) {
return formatError(value);
}
// Some type of object without properties can be shortcutted.
if (keys.length === 0) {
if (isFunction(value)) {
var name = value.name ? ': ' + value.name : '';
return ctx.stylize('[Function' + name + ']', 'special');
}
if (isRegExp(value)) {
return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
}
if (isDate(value)) {
return ctx.stylize(Date.prototype.toString.call(value), 'date');
}
if (isError(value)) {
return formatError(value);
}
}
var base = '', array = false, braces = ['{', '}'];
// Make Array say that they are Array
if (isArray(value)) {
array = true;
braces = ['[', ']'];
}
// Make functions say that they are functions
if (isFunction(value)) {
var n = value.name ? ': ' + value.name : '';
base = ' [Function' + n + ']';
}
// Make RegExps say that they are RegExps
if (isRegExp(value)) {
base = ' ' + RegExp.prototype.toString.call(value);
}
// Make dates with properties first say the date
if (isDate(value)) {
base = ' ' + Date.prototype.toUTCString.call(value);
}
// Make error with message first say the error
if (isError(value)) {
base = ' ' + formatError(value);
}
if (keys.length === 0 && (!array || value.length == 0)) {
return braces[0] + base + braces[1];
}
if (recurseTimes < 0) {
if (isRegExp(value)) {
return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
} else {
return ctx.stylize('[Object]', 'special');
}
}
ctx.seen.push(value);
var output;
if (array) {
output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
} else {
output = keys.map(function(key) {
return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
});
}
ctx.seen.pop();
return reduceToSingleString(output, base, braces);
}
function formatPrimitive(ctx, value) {
if (isUndefined(value))
return ctx.stylize('undefined', 'undefined');
if (isString(value)) {
var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
.replace(/'/g, "\\'")
.replace(/\\"/g, '"') + '\'';
return ctx.stylize(simple, 'string');
}
if (isNumber(value))
return ctx.stylize('' + value, 'number');
if (isBoolean(value))
return ctx.stylize('' + value, 'boolean');
// For some reason typeof null is "object", so special case here.
if (isNull(value))
return ctx.stylize('null', 'null');
}
function formatError(value) {
return '[' + Error.prototype.toString.call(value) + ']';
}
function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
var output = [];
for (var i = 0, l = value.length; i < l; ++i) {
if (hasOwnProperty(value, String(i))) {
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
String(i), true));
} else {
output.push('');
}
}
keys.forEach(function(key) {
if (!key.match(/^\d+$/)) {
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
key, true));
}
});
return output;
}
function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
var name, str, desc;
desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] };
if (desc.get) {
if (desc.set) {
str = ctx.stylize('[Getter/Setter]', 'special');
} else {
str = ctx.stylize('[Getter]', 'special');
}
} else {
if (desc.set) {
str = ctx.stylize('[Setter]', 'special');
}
}
if (!hasOwnProperty(visibleKeys, key)) {
name = '[' + key + ']';
}
if (!str) {
if (ctx.seen.indexOf(desc.value) < 0) {
if (isNull(recurseTimes)) {
str = formatValue(ctx, desc.value, null);
} else {
str = formatValue(ctx, desc.value, recurseTimes - 1);
}
if (str.indexOf('\n') > -1) {
if (array) {
str = str.split('\n').map(function(line) {
return ' ' + line;
}).join('\n').substr(2);
} else {
str = '\n' + str.split('\n').map(function(line) {
return ' ' + line;
}).join('\n');
}
}
} else {
str = ctx.stylize('[Circular]', 'special');
}
}
if (isUndefined(name)) {
if (array && key.match(/^\d+$/)) {
return str;
}
name = JSON.stringify('' + key);
if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
name = name.substr(1, name.length - 2);
name = ctx.stylize(name, 'name');
} else {
name = name.replace(/'/g, "\\'")
.replace(/\\"/g, '"')
.replace(/(^"|"$)/g, "'");
name = ctx.stylize(name, 'string');
}
}
return name + ': ' + str;
}
function reduceToSingleString(output, base, braces) {
var numLinesEst = 0;
var length = output.reduce(function(prev, cur) {
numLinesEst++;
if (cur.indexOf('\n') >= 0) numLinesEst++;
return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
}, 0);
if (length > 60) {
return braces[0] +
(base === '' ? '' : base + '\n ') +
' ' +
output.join(',\n ') +
' ' +
braces[1];
}
return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
}
// NOTE: These type checking functions intentionally don't use `instanceof`
// because it is fragile and can be easily faked with `Object.create()`.
function isArray(ar) {
return Array.isArray(ar);
}
exports.isArray = isArray;
function isBoolean(arg) {
return typeof arg === 'boolean';
}
exports.isBoolean = isBoolean;
function isNull(arg) {
return arg === null;
}
exports.isNull = isNull;
function isNullOrUndefined(arg) {
return arg == null;
}
exports.isNullOrUndefined = isNullOrUndefined;
function isNumber(arg) {
return typeof arg === 'number';
}
exports.isNumber = isNumber;
function isString(arg) {
return typeof arg === 'string';
}
exports.isString = isString;
function isSymbol(arg) {
return typeof arg === 'symbol';
}
exports.isSymbol = isSymbol;
function isUndefined(arg) {
return arg === void 0;
}
exports.isUndefined = isUndefined;
function isRegExp(re) {
return isObject(re) && objectToString(re) === '[object RegExp]';
}
exports.isRegExp = isRegExp;
function isObject(arg) {
return typeof arg === 'object' && arg !== null;
}
exports.isObject = isObject;
function isDate(d) {
return isObject(d) && objectToString(d) === '[object Date]';
}
exports.isDate = isDate;
function isError(e) {
return isObject(e) &&
(objectToString(e) === '[object Error]' || e instanceof Error);
}
exports.isError = isError;
function isFunction(arg) {
return typeof arg === 'function';
}
exports.isFunction = isFunction;
function isPrimitive(arg) {
return arg === null ||
typeof arg === 'boolean' ||
typeof arg === 'number' ||
typeof arg === 'string' ||
typeof arg === 'symbol' || // ES6 symbol
typeof arg === 'undefined';
}
exports.isPrimitive = isPrimitive;
exports.isBuffer = require('./support/isBuffer');
function objectToString(o) {
return Object.prototype.toString.call(o);
}
function pad(n) {
return n < 10 ? '0' + n.toString(10) : n.toString(10);
}
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
'Oct', 'Nov', 'Dec'];
// 26 Feb 16:19:34
function timestamp() {
var d = new Date();
var time = [pad(d.getHours()),
pad(d.getMinutes()),
pad(d.getSeconds())].join(':');
return [d.getDate(), months[d.getMonth()], time].join(' ');
}
// log is just a thin wrapper to console.log that prepends a timestamp
exports.log = function() {
console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
};
/**
* Inherit the prototype methods from one constructor into another.
*
* The Function.prototype.inherits from lang.js rewritten as a standalone
* function (not on Function.prototype). NOTE: If this file is to be loaded
* during bootstrapping this function needs to be rewritten using some native
* functions as prototype setup using normal JavaScript does not work as
* expected during bootstrapping (see mirror.js in r114903).
*
* @param {function} ctor Constructor function which needs to inherit the
* prototype.
* @param {function} superCtor Constructor function to inherit prototype from.
*/
exports.inherits = require('inherits');
exports._extend = function(origin, add) {
// Don't do anything if add isn't an object
if (!add || !isObject(add)) return origin;
var keys = Object.keys(add);
var i = keys.length;
while (i--) {
origin[keys[i]] = add[keys[i]];
}
return origin;
};
function hasOwnProperty(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./support/isBuffer":5985,"_process":5966,"inherits":5964}],"itags":[function(require,module,exports){
(function (global){
/**
* The ITSA module is an aggregator for all the individual modules that the library uses.
* The developer is free to use it as it is or tailor it to contain whatever modules
* he/she might need in the global namespace.
*
* The modules themselves work quite well independent of this module and can be used
* separately without the need of them being integrated under one globa namespace.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @module itsa.build
*
*/
(function (window) {
"use strict";
require('i-tabpane')(window);
require('i-select')(window);
require('i-parcel')(window);
require('i-label')(window);
require('i-input')(window);
require('i-button')(window);
require('i-reset')(window);
require('i-form')(window);
require('i-checkbox')(window);
require('i-statusbar')(window);
})(global.window || require('node-win'));
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"i-button":5935,"i-checkbox":5937,"i-form":5939,"i-input":5942,"i-label":5943,"i-parcel":5944,"i-reset":5945,"i-select":5947,"i-statusbar":5949,"i-tabpane":5951,"node-win":5923}]},{},[]);